diff --git a/hnv_client/client.py b/hnv_client/client.py index 6e7a65e..5e57693 100644 --- a/hnv_client/client.py +++ b/hnv_client/client.py @@ -738,3 +738,135 @@ class NetworkInterfaces(_BaseHNVModel): properties["configurationState"] = configuration return super(NetworkInterfaces, cls).from_raw_data(raw_data) + + +class SubNetworks(_BaseHNVModel): + + """SubNetwork Model. + + The subnets resource is used to create Virtual Subnets (VSIDs) under + a tenant's virtual network (RDID). The user can specify the addressPrefix + to use for the subnets, the accessControl Lists to protect the subnets, + the routeTable to be applied to the subnet, and optionally the service + insertion to use within the subnet. + """ + + _endpoint = ("/networking/v1/virtualNetworks/{parent_id}" + "/subnets/{resource_id}") + + parent_id = model.Field(name="parent_id", + key="parentResourceID", + is_property=False, is_required=True, + is_read_only=True) + """The parent resource ID field contains the resource ID that is + associated with network objects that are ancestors of the necessary + resource. + """ + + address_prefix = model.Field(name="address_prefix", key="addressPrefix", + is_required=True) + """Indicates the address prefix that defines the subnet. The value is + in the format of 0.0.0.0/0. This value must not overlap with other + subnets in the virtual network and must fall in the addressPrefix defined + in the virtual network.""" + + access_controll_list = model.Field(name="access_controll_list", + key="accessControlList", + is_required=False) + """Indicates a reference to an accessControlLists resource that defines + the ACLs in and out of the subnet.""" + + service_insertion = model.Field(name="service_insertion", + key="serviceInsertion", + is_required=False) + """Indicates a reference to a serviceInsertions resource that defines the + service insertion to be applied to the subnet.""" + + route_table = model.Field(name="route_table", key="routeTable", + is_required=False) + """Indicates a reference to a routeTable resource that defines the tenant + routes to be applied to the subnet.""" + + ip_configuration = model.Field(name="ip_configuration", + key="ipConfigurations", + is_read_only=False) + """Indicates an array of references of networkInterfaces resources that + are connected to the subnet.""" + + @classmethod + def from_raw_data(cls, raw_data): + """Create a new model using raw API response.""" + properties = raw_data["properties"] + + ip_configurations = [] + for raw_config in properties.get("ipConfigurations", []): + ip_configurations.append(IPConfiguration.from_raw_data(raw_config)) + properties["ipConfigurations"] = ip_configurations + + acl = properties.get("accessControlList") + if acl: + properties["accessControlList"] = Resource.from_raw_data(acl) + + return super(SubNetworks, cls).from_raw_data(raw_data) + + +class VirtualNetworks(_BaseHNVModel): + + """Virtual Network Model. + + This resource is used to create a virtual network using HNV for tenant + overlays. The default encapsulation for virtualNetworks is Virtual + Extensible LAN but this can be changed by updating the virtual + NetworkManager resource. Similarly, the HNV Distributed Router is enabled + by default but this can be overridden using the virtualNetworkManager + resource. + """ + + _endpoint = "/networking/v1/virtualNetworks/{resource_id}" + + configuration_state = model.Field(name="configuration_state", + key="configurationState", + is_read_only=True) + """Indicates the last known running state of this resource.""" + + address_space = model.Field(name="address_space", + key="addressSpace", + is_required=True) + """Indicates the address space of the virtual network.""" + + dhcp_options = model.Field(name="dhcp_options", key="dhcpOptions", + is_required=False) + """Indicates the DHCP options used by servers in the virtual + network.""" + + subnetworks = model.Field(name="subnetworks", key="subnets", + is_required=False) + """Indicates the subnets that are on the virtual network.""" + + logical_network = model.Field(name="logical_network", + key="logicalNetwork", + is_required=True) + """Indicates a reference to the networks resource that is the + underlay network which the virtual network runs on.""" + + @classmethod + def from_raw_data(cls, raw_data): + """Create a new model using raw API response.""" + properties = raw_data["properties"] + + subnetworks = [] + for raw_subnet in properties.get("subnets", []): + raw_subnet["parentResourceID"] = raw_data["resourceId"] + subnetworks.append(SubNetworks.from_raw_data(raw_subnet)) + properties["subnets"] = subnetworks + + raw_network = properties.get("logicalNetwork") + if raw_network: + properties["logicalNetwork"] = Resource.from_raw_data(raw_network) + + raw_config = properties.get("configurationState") + if raw_config: + config = ConfigurationState.from_raw_data(raw_config) + properties["configurationState"] = config + + return super(VirtualNetworks, cls).from_raw_data(raw_data) diff --git a/hnv_client/common/model.py b/hnv_client/common/model.py index e225492..633bd32 100644 --- a/hnv_client/common/model.py +++ b/hnv_client/common/model.py @@ -280,9 +280,11 @@ class Model(object): content[field_name] = value if raw_data: - LOG.debug("Unrecognized fields: %r", raw_data) + LOG.debug("Unrecognized fields: %r for %r", + raw_data, cls.__name__) if properties: - LOG.debug("Unrecognized properties: %r", properties) + LOG.debug("Unrecognized properties: %r for %r", + properties, cls.__name__) return content diff --git a/hnv_client/tests/fake/fake_response.py b/hnv_client/tests/fake/fake_response.py index cb9f7a8..2b7bd98 100644 --- a/hnv_client/tests/fake/fake_response.py +++ b/hnv_client/tests/fake/fake_response.py @@ -53,3 +53,11 @@ class FakeResponse(object): def ip_configurations(self): """Fake GET(all) response for ip configurations.""" return self._load_resource("ip_configurations.json") + + def virtual_networks(self): + """Fake GET(all) response for virtual networks.""" + return self._load_resource("virtual_networks.json") + + def virtual_subnetworks(self): + """Fake GET(all) response for virtual subnetworks.""" + return self._load_resource("virtual_subnetworks.json") diff --git a/hnv_client/tests/fake/response/virtual_networks.json b/hnv_client/tests/fake/response/virtual_networks.json new file mode 100644 index 0000000..fd4fa50 --- /dev/null +++ b/hnv_client/tests/fake/response/virtual_networks.json @@ -0,0 +1,229 @@ +{ + "value": [ + { + "resourceRef": "/virtualNetworks/2c40fb79-6488-4804-980a-a178a8e123f4", + "resourceId": "2c40fb79-6488-4804-980a-a178a8e123f4", + "etag": "W/\"f183dbae-3908-4a08-b2d3-7f73bae97cab\"", + "instanceId": "e5a0bb17-f781-4dc2-9f11-f472d61f8470", + "properties": { + "provisioningState": "Succeeded", + "addressSpace": { + "addressPrefixes": [ + "13.168.100.0/24", + "13.168.101.0/24" + ] + }, + "dhcpOptions": {}, + "configurationState": { + "status": "Failure", + "lastUpdatedTime": "2016-06-14T19:12:06.400512-07:00", + "id": "368ebe7d-38de-48f8-a0d8-b3b816a4b1ea", + "virtualNetworkInterfaceErrors": [ + { + "status": "Failure", + "detailedInfo": [ + { + "source": "VirtualNetwork", + "message": "Failed to configure the policies on the host device", + "code": "PolicyConfigurationFailure" + }, + { + "source": "VirtualNetwork2", + "message": "Failed to configure the policies on the host device", + "code": "PolicyConfigurationFailure2" + } + ], + "lastUpdatedTime": "2016-06-14T19:12:06.400512-07:00", + "id": "c7ab848f-e522-47cd-b9f6-5a2c7749a73f" + }, + { + "status": "Failure", + "detailedInfo": [ + { + "source": "VirtualNetwork", + "message": "Failed to configure the policies on the host device", + "code": "PolicyConfigurationFailure" + } + ], + "lastUpdatedTime": "2016-06-14T19:12:06.400512-07:00", + "id": "5ef191d3-6ec6-4246-984c-8d6a19da301f" + }, + { + "status": "Failure", + "detailedInfo": [ + { + "source": "VirtualNetwork", + "message": "Failed to configure the policies on the host device", + "code": "PolicyConfigurationFailure" + } + ], + "lastUpdatedTime": "2016-06-14T19:12:06.400512-07:00", + "id": "4058b793-6c28-43d4-a957-937d453075d7" + }, + { + "status": "Failure", + "detailedInfo": [ + { + "source": "VirtualNetwork", + "message": "Failed to configure the policies on the host device", + "code": "PolicyConfigurationFailure" + } + ], + "lastUpdatedTime": "2016-06-14T19:12:06.400512-07:00", + "id": "2a9e39e6-8258-42b8-9db2-31bb2e3932c4" + } + ], + "hostErrors": [ + { + "status": "Failure", + "detailedInfo": [ + { + "source": "VirtualNetwork", + "message": "Failed to configure the policies on the host device.", + "code": "PolicyConfigurationFailure" + } + ], + "lastUpdatedTime": "2016-06-14T19:12:06.400512-07:00", + "id": "6af6ddf0-cd09-44d8-917f-97de215f7c9d" + } + ] + }, + "subnets": [ + { + "resourceRef": "/virtualNetworks/2c40fb79-6488-4804-980a-a178a8e123f4/subnets/1b466669-3c06-4e34-b0c9-d737591ecc2c", + "resourceId": "1b466669-3c06-4e34-b0c9-d737591ecc2c", + "etag": "W/\"f183dbae-3908-4a08-b2d3-7f73bae97cab\"", + "instanceId": "9db21d13-63ce-4571-9674-930663dafa90", + "properties": { + "provisioningState": "Succeeded", + "addressPrefix": "13.168.100.0/24", + "accessControlList": { + "resourceRef": "/accessControlLists/0879bb16-0cdc-435a-88ff-ef24813201d9" + }, + "ipConfigurations": [ + { + "resourceRef": "/networkInterfaces/7cc631c8-ca6b-4d21-b1f8-5b0373d32301/ipConfigurations/18e3af43-be4a-4116-882c-d7257a8bc72b" + }, + { + "resourceRef": "/networkInterfaces/6ebf2132-2871-4535-b412-b6e255bcafa2/ipConfigurations/74fe0850-09a0-4526-9d43-906cd4e6f52a" + }, + { + "resourceRef": "/networkInterfaces/c55a70de-34a7-4260-be7b-76e4b65f32c6/ipConfigurations/486734ba-5521-4348-81a9-3158e2b7fa6e" + }, + { + "resourceRef": "/networkInterfaces/d9a8a624-9356-4f4e-bd88-fcde1574dba3/ipConfigurations/11aa8ca8-b684-4ca0-b35d-4e7db62e7b6f" + } + ] + } + }, + { + "resourceRef": "/virtualNetworks/2c40fb79-6488-4804-980a-a178a8e123f4/subnets/9c01100a-2bbc-4388-adb2-6cbcdee3447f", + "resourceId": "9c01100a-2bbc-4388-adb2-6cbcdee3447f", + "etag": "W/\"f183dbae-3908-4a08-b2d3-7f73bae97cab\"", + "instanceId": "0ef3bac9-3496-40ec-aeff-3403ea6541ef", + "properties": { + "provisioningState": "Succeeded", + "addressPrefix": "13.168.101.0/24", + "accessControlList": { + "resourceRef": "/accessControlLists/0879bb16-0cdc-435a-88ff-ef24813201d9" + }, + "ipConfigurations": [ + { + "resourceRef": "/networkInterfaces/447843e7-3fe4-4337-aac5-72e38258d6a4/ipConfigurations/31bb0476-a4d4-4a9a-8d98-3a47dea56f59" + }, + { + "resourceRef": "/networkInterfaces/7a4ba9a1-7542-42f9-b718-80de763001cb/ipConfigurations/833540aa-5037-490f-96b9-6a7d78faa762" + }, + { + "resourceRef": "/networkInterfaces/3157a320-6a05-463f-8c32-5af4759fbf88/ipConfigurations/fe4536ec-8443-4393-b534-2e035bbe6aaf" + }, + { + "resourceRef": "/networkInterfaces/125f3909-8fc9-4ab4-b46c-3e8d39b52de2/ipConfigurations/7cca0ee7-dbcd-4d25-a211-8c26708093ca" + } + ] + } + } + ], + "logicalNetwork": { + "resourceRef": "/logicalnetworks/dbbd37e2-031e-43b3-a16a-d167caca0067" + } + } + }, + { + "resourceRef": "/virtualNetworks/88e38f44-a55b-4604-af5b-83d44bb32508", + "resourceId": "88e38f44-a55b-4604-af5b-83d44bb32508", + "etag": "W/\"f940af0b-194b-4264-b581-cf9ecd02417d\"", + "instanceId": "77ccbb79-a7a2-432d-af08-cde9b6fbf89c", + "properties": { + "provisioningState": "Succeeded", + "addressSpace": { + "addressPrefixes": [ + "13.168.100.0/24", + "13.168.101.0/24" + ] + }, + "dhcpOptions": {}, + "subnets": [ + { + "resourceRef": "/virtualNetworks/88e38f44-a55b-4604-af5b-83d44bb32508/subnets/32e2069d-b05c-4090-9f2a-dd1d9e076c18", + "resourceId": "32e2069d-b05c-4090-9f2a-dd1d9e076c18", + "etag": "W/\"f940af0b-194b-4264-b581-cf9ecd02417d\"", + "instanceId": "30acab53-f9ef-4a8b-b349-5152d4ca0847", + "properties": { + "provisioningState": "Succeeded", + "addressPrefix": "13.168.100.0/24", + "accessControlList": { + "resourceRef": "/accessControlLists/00000000-0000-BAAD-F00D-000000000000" + }, + "ipConfigurations": [ + { + "resourceRef": "/networkInterfaces/35cd19a9-a47b-457c-a616-b19dfb80a284/ipConfigurations/36bb234c-3594-486f-bfd8-84aee4f15c55" + }, + { + "resourceRef": "/networkInterfaces/6065ddd9-9574-422a-8ff7-cfb51275ebd5/ipConfigurations/60ce029d-d7ff-482d-88f7-7baca89f6d47" + }, + { + "resourceRef": "/networkInterfaces/4f937e27-dbbc-401f-8acf-60eb1b7f42f2/ipConfigurations/90db0417-9067-449a-bc19-776f07707497" + }, + { + "resourceRef": "/networkInterfaces/dda65508-b384-4215-b6cc-23c442d0b185/ipConfigurations/7bda1749-a1ed-4489-b871-c1378bae5f33" + } + ] + } + }, + { + "resourceRef": "/virtualNetworks/88e38f44-a55b-4604-af5b-83d44bb32508/subnets/45819314-35b0-47ff-8447-3c78ed3ad8eb", + "resourceId": "45819314-35b0-47ff-8447-3c78ed3ad8eb", + "etag": "W/\"f940af0b-194b-4264-b581-cf9ecd02417d\"", + "instanceId": "ba555875-c564-4987-94a5-a0e260d7e2af", + "properties": { + "provisioningState": "Succeeded", + "addressPrefix": "13.168.101.0/24", + "accessControlList": { + "resourceRef": "/accessControlLists/949fc25d-0675-4af4-b989-2bf653b795eb" + }, + "ipConfigurations": [ + { + "resourceRef": "/networkInterfaces/e8a7fea7-e4f9-4742-9e89-aced72ee5a57/ipConfigurations/a9fbf102-6646-442b-8631-6c0c2c193b35" + }, + { + "resourceRef": "/networkInterfaces/f94421e8-3efb-42dc-b7dd-aaa61f1f32e5/ipConfigurations/ea5d80da-70da-4592-8d07-ce31b38808e4" + }, + { + "resourceRef": "/networkInterfaces/d9259a46-b685-4b40-ad0d-2afd74fbf6b3/ipConfigurations/34f81b26-ad6b-4dbf-b5d7-2ca3c5bbf9cf" + }, + { + "resourceRef": "/networkInterfaces/9be77260-a529-4162-b2a2-f04495a200da/ipConfigurations/fff40242-ca47-4e91-a206-3d11f2c49c7e" + } + ] + } + } + ], + "logicalNetwork": { + "resourceRef": "/logicalnetworks/dbbd37e2-031e-43b3-a16a-d167caca0067" + } + } + } + ], + "nextLink": "" +} \ No newline at end of file diff --git a/hnv_client/tests/fake/response/virtual_subnetworks.json b/hnv_client/tests/fake/response/virtual_subnetworks.json new file mode 100644 index 0000000..ef713a7 --- /dev/null +++ b/hnv_client/tests/fake/response/virtual_subnetworks.json @@ -0,0 +1,86 @@ +{ + "value": [ + { + "resourceRef": "/virtualNetworks/740f3670-de42-4345-aaa7-6bb8d423c5df/subnets/f144bb56- 9868-48f7-af38-73d331e780cc", + "resourceId": "f144bb56-9868-48f7-af38-73d331e780cc", + "etag": "W/\"63e97aed-2900-46d3-8667-ef183d773655\"", + "instanceId": "bd2a55ed-47ad-478a-b7ee-c0ed3e14ca69", + "properties": { + "provisioningState": "Succeeded", + "addressPrefix": "13.168.100.0/24", + "accessControlList": { + "resourceRef": "/accessControlLists/b79fe2f0-8f27-4521-9c8c-4c02be8c62eb" + }, + "ipConfigurations": [ + { + "resourceRef": "/networkInterfaces/350ab978-a032-402e-96cb- ad48fbdce219/ipConfigurations/340229d1-fb10-46a6-bf83-e752d76871cd" + }, + { + "resourceRef": "/networkInterfaces/519d1b64-f99d-430b-b626- 347ef7690ee1/ipConfigurations/8420d069-6414-43f7-bbaf-5c1f5cc9b434" + }, + { + "resourceRef": "/networkInterfaces/bc0b4ec5-8d40-4b62-bb1c- 09181bb1ca57/ipConfigurations/bbda3955-5c56-454b-956c-ab576fea1c8d" + }, + { + "resourceRef": "/networkInterfaces/1e03dd1d-c4c4-4153-a1c8- d692d8e340ab/ipConfigurations/a6d79d5e-b266-47a1-83e1-e61f8784f882" + } + ] + } + }, + { + "resourceRef": "/virtualNetworks/740f3670-de42-4345-aaa7-6bb8d423c5df/subnets/da459373- 42ee-43d3-b094-6e2176406e4a", + "resourceId": "da459373-42ee-43d3-b094-6e2176406e4a", + "etag": "W/\"63e97aed-2900-46d3-8667-ef183d773655\"", + "instanceId": "b526c5e7-927c-4d74-be86-cd2933ac286d", + "properties": { + "provisioningState": "Succeeded", + "addressPrefix": "13.168.101.0/24", + "accessControlList": { + "resourceRef": "/accessControlLists/b79fe2f0-8f27-4521-9c8c-4c02be8c62eb" + }, + "ipConfigurations": [ + { + "resourceRef": "/networkInterfaces/178480e8-cb41-4105-9ce9- d3c4051b1e16/ipConfigurations/5d24f2a5-557c-4692-86d7-dce921ef7e57" + }, + { + "resourceRef": "/networkInterfaces/f7957eeb-55b0-46dd-8ef8- 0bb0127c55d1/ipConfigurations/8dd5a2e6-5d83-43b5-ad5b-c08a2fa26935" + }, + { + "resourceRef": "/networkInterfaces/ec3ac77e-64be-4bc1-a2e3- 7cd6170a4752/ipConfigurations/cbcab016-6c87-4a32-8158-08e0db71635a" + }, + { + "resourceRef": "/networkInterfaces/caa5e37a-30ce-4c0a-877c- d21b7c732bce/ipConfigurations/aa0eff2d-00f6-413b-9650-7e13e3d31ead" + } + ] + } + }, + { + "resourceRef": "/virtualNetworks/740f3670-de42-4345-aaa7-6bb8d423c5df/subnets/da459373-42ee-43d3-b094-6e2176406e4a", + "resourceId": "da459373-42ee-43d3-b094-6e2176406e4a", + "etag": "W/\"63e97aed-2900-46d3-8667-ef183d773655\"", + "instanceId": "b526c5e7-927c-4d74-be86-cd2933ac286d", + "properties": { + "provisioningState": "Succeeded", + "addressPrefix": "13.168.101.0/24", + "accessControlList": { + "resourceRef": "/accessControlLists/b79fe2f0-8f27-4521-9c8c-4c02be8c62eb" + }, + "ipConfigurations": [ + { + "resourceRef": "/networkInterfaces/178480e8-cb41-4105-9ce9-d3c4051b1e16/ipConfigurations/5d24f2a5-557c-4692-86d7-dce921ef7e57" + }, + { + "resourceRef": "/networkInterfaces/f7957eeb-55b0-46dd-8ef8-0bb0127c55d1/ipConfigurations/8dd5a2e6-5d83-43b5-ad5b-c08a2fa26935" + }, + { + "resourceRef": "/networkInterfaces/ec3ac77e-64be-4bc1-a2e3-7cd6170a4752/ipConfigurations/cbcab016-6c87-4a32-8158-08e0db71635a" + }, + { + "resourceRef": "/networkInterfaces/caa5e37a-30ce-4c0a-877c-d21b7c732bce/ipConfigurations/aa0eff2d-00f6-413b-9650-7e13e3d31ead" + } + ] + } + } + ], + "nextLink": "" +} \ No newline at end of file diff --git a/hnv_client/tests/test_client.py b/hnv_client/tests/test_client.py index 14d4964..7e17a5a 100644 --- a/hnv_client/tests/test_client.py +++ b/hnv_client/tests/test_client.py @@ -239,3 +239,15 @@ class TestClient(unittest.TestCase): for raw_data in resources.get("value", []): self._test_get_resource(model=client.IPConfiguration, raw_data=raw_data) + + def test_virtual_networks(self): + resources = self._response.virtual_networks() + for raw_data in resources.get("value", []): + self._test_get_resource(model=client.VirtualNetworks, + raw_data=raw_data) + + def test_virtual_subnetworks(self): + resources = self._response.virtual_subnetworks() + for raw_data in resources.get("value", []): + self._test_get_resource(model=client.SubNetworks, + raw_data=raw_data)