diff --git a/bin/melange-client b/bin/melange-client index 2afde854..22f5e3fb 100755 --- a/bin/melange-client +++ b/bin/melange-client @@ -122,6 +122,7 @@ categories = { 'allocated_ips': client.AllocatedIpAddressesClient, 'ip_address': client.IpAddressesClient, 'ip_route': client.IpRouteClient, + 'interface': client.InterfaceClient, } @@ -189,7 +190,7 @@ def main(): print_keys(client_actions) sys.exit(2) - if category_client_class.is_tenant_id_required() and not options.tenant: + if category_client_class.TENANT_ID_REQUIRED and not options.tenant: print _("Please provide a tenant id for this action." "You can use option '-t' to provide the tenant id.") sys.exit(2) diff --git a/melange/ipam/client.py b/melange/ipam/client.py index e134c2de..4488ad3a 100644 --- a/melange/ipam/client.py +++ b/melange/ipam/client.py @@ -66,15 +66,13 @@ class Resource(object): class BaseClient(object): + TENANT_ID_REQUIRED = True + def __init__(self, client, auth_client, tenant_id): self.client = client self.auth_client = auth_client self.tenant_id = tenant_id - @classmethod - def is_tenant_id_required(cls): - return True - class IpBlockClient(BaseClient): @@ -199,6 +197,8 @@ class UnusableIpOctetsClient(BaseClient): class AllocatedIpAddressesClient(BaseClient): + TENANT_ID_REQUIRED = False + def __init__(self, client, auth_client, tenant_id=None): self._resource = Resource("allocated_ip_addresses", "allocated_ip_addresses", @@ -206,10 +206,6 @@ class AllocatedIpAddressesClient(BaseClient): auth_client, tenant_id) - @classmethod - def is_tenant_id_required(cls): - return False - def list(self, used_by_device=None): return self._resource.all(used_by_device=used_by_device) @@ -266,3 +262,29 @@ class IpRouteClient(BaseClient): def delete(self, ip_block_id, route_id): return self._resource(ip_block_id).delete(route_id) + + +class InterfaceClient(BaseClient): + + TENANT_ID_REQUIRED = False + + def __init__(self, client, auth_client, tenant_id=None): + self._resource = Resource("interfaces", + "interface", + client, + auth_client, + tenant_id) + + def create(self, vif_id, tenant_id, device_id=None, network_id=None): + request_params = dict(id=vif_id, tenant_id=tenant_id, + device_id=device_id) + if network_id: + request_params['network'] = dict(id=network_id) + + return self._resource.create(**request_params) + + def show(self, vif_id): + return self._resource.find(vif_id) + + def delete(self, vif_id): + return self._resource.delete(vif_id) diff --git a/melange/ipam/service.py b/melange/ipam/service.py index 0cad06c2..c485d5d8 100644 --- a/melange/ipam/service.py +++ b/melange/ipam/service.py @@ -386,7 +386,7 @@ class InterfacesController(BaseController): def create(self, request, body=None): params = self._extract_required_params(body, 'interface') - params['virtual_interface_id'] = params.pop('interface_id', None) + params['virtual_interface_id'] = params.pop('id', None) network_params = utils.stringify_keys(params.pop('network', None)) interface = models.Interface.create_and_configure(**params) diff --git a/melange/tests/functional/test_cli.py b/melange/tests/functional/test_cli.py index e4185859..1aae765e 100644 --- a/melange/tests/functional/test_cli.py +++ b/melange/tests/functional/test_cli.py @@ -442,6 +442,58 @@ class TestIpRoutesCLI(tests.BaseTest): self.assertIsNone(models.IpRoute.get(ip_route.id)) +class TestInterfaceCLI(tests.BaseTest): + + def test_create(self): + mac_range = factory_models.MacAddressRangeFactory() + exitcode, out, err = run("interface create vif_id tenant_id " + "device_id network_id") + + self.assertEqual(exitcode, 0) + created_interface = models.Interface.find_by( + virtual_interface_id="vif_id") + + self.assertEqual(created_interface.tenant_id, "tenant_id") + self.assertEqual(created_interface.device_id, "device_id") + self.assertIsNotNone(created_interface.mac_address_eui_format) + self.assertIsNotNone(created_interface.ip_addresses) + + def test_show(self): + interface = factory_models.InterfaceFactory( + virtual_interface_id="vif_id", tenant_id="tenant_id") + mac = models.MacAddress.create(address="ab-bc-cd-12-23-34", + interface_id=interface.id) + ip1 = factory_models.IpAddressFactory(interface_id=interface.id) + ip2 = factory_models.IpAddressFactory(interface_id=interface.id) + noise_ip = factory_models.IpAddressFactory() + + exitcode, out, err = run("interface show vif_id -t tenant_id") + + self.assertEqual(exitcode, 0) + self.assertIn("vif_id", out) + self.assertIn(mac.eui_format, out) + self.assertIn(ip1.address, out) + self.assertIn(ip2.address, out) + self.assertNotIn(noise_ip.address, out) + + def test_delete(self): + interface = factory_models.InterfaceFactory( + virtual_interface_id="vif_id") + mac = models.MacAddress.create(address="ab-bc-cd-12-23-34", + interface_id=interface.id) + ip1 = factory_models.IpAddressFactory(interface_id=interface.id) + ip2 = factory_models.IpAddressFactory(interface_id=interface.id) + noise_ip = factory_models.IpAddressFactory() + + exitcode, out, err = run("interface delete vif_id") + + self.assertEqual(exitcode, 0) + self.assertIsNone(models.Interface.get(interface.id)) + self.assertIsNone(models.MacAddress.get(mac.id)) + self.assertTrue(models.IpAddress.get(ip1.id).marked_for_deallocation) + self.assertTrue(models.IpAddress.get(ip2.id).marked_for_deallocation) + + class TestDBSyncCLI(tests.BaseTest): def test_db_sync_executes(self): diff --git a/melange/tests/unit/test_ipam_service.py b/melange/tests/unit/test_ipam_service.py index 19e829b9..8c85aefa 100644 --- a/melange/tests/unit/test_ipam_service.py +++ b/melange/tests/unit/test_ipam_service.py @@ -2035,7 +2035,7 @@ class TestInterfacesController(BaseTestController): def test_create_interface(self): response = self.app.post_json("/ipam/interfaces", {'interface': { - 'interface_id': "virt_iface", + 'id': "virt_iface", 'device_id': "instance", 'tenant_id': "tnt", } @@ -2056,7 +2056,7 @@ class TestInterfacesController(BaseTestController): self.app.post_json("/ipam/interfaces", {'interface': { - 'interface_id': "virt_iface", + 'id': "virt_iface", 'device_id': "instance", 'tenant_id': "tnt_id", 'network': { @@ -2076,7 +2076,7 @@ class TestInterfacesController(BaseTestController): factory_models.MacAddressRangeFactory() self.app.post_json("/ipam/interfaces", {'interface': { - 'interface_id': "virt_iface", + 'id': "virt_iface", 'device_id': "instance", 'tenant_id': "tnt", } @@ -2093,7 +2093,7 @@ class TestInterfacesController(BaseTestController): tenant_id="tnt1") self.app.post_json("/ipam/interfaces", {'interface': { - 'interface_id': "virt_iface", + 'id': "virt_iface", 'device_id': "instance", 'tenant_id': "tnt1", 'network': {'id': "net1"} @@ -2122,7 +2122,7 @@ class TestInterfacesController(BaseTestController): self.app.post_json("/ipam/interfaces", {'interface': { - 'interface_id': "virt_iface", + 'id': "virt_iface", 'device_id': "instance", 'tenant_id': "tnt_id", 'mac_address': mac_address, @@ -2141,7 +2141,7 @@ class TestInterfacesController(BaseTestController): def test_create_when_network_not_found_creates_default_cidr_block(self): self.app.post_json("/ipam/interfaces", {'interface': { - 'interface_id': "virt_iface", + 'id': "virt_iface", 'device_id': "instance", 'tenant_id': "tnt_id", 'network': {'id': "net1"},