diff --git a/gbpautomation/heat/engine/__init__.py b/gbpautomation/heat/engine/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/gbpautomation/heat/engine/clients/__init__.py b/gbpautomation/heat/engine/clients/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/gbpautomation/heat/engine/clients/os/__init__.py b/gbpautomation/heat/engine/clients/os/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/gbpautomation/heat/engine/clients/os/grouppolicy.py b/gbpautomation/heat/engine/clients/os/grouppolicy.py new file mode 100644 index 0000000..6d6fa19 --- /dev/null +++ b/gbpautomation/heat/engine/clients/os/grouppolicy.py @@ -0,0 +1,60 @@ +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +from gbpclient.v2_0 import client as gbpc +from neutronclient.common import exceptions + +from heat.engine.clients import client_plugin + + +class GBPClientPlugin(client_plugin.ClientPlugin): + + exceptions_module = exceptions + + def _create(self): + + con = self.context + + endpoint_type = self._get_client_option('grouppolicy', 'endpoint_type') + endpoint = self.url_for(service_type='network', + endpoint_type=endpoint_type) + + args = { + 'auth_url': con.auth_url, + 'service_type': 'network', + 'token': self.auth_token, + 'endpoint_url': endpoint, + 'endpoint_type': endpoint_type, + 'ca_cert': self._get_client_option('grouppolicy', 'ca_file'), + 'insecure': self._get_client_option('grouppolicy', 'insecure') + } + + return gbpc.Client(**args) + + def is_not_found(self, ex): + if isinstance(ex, (exceptions.NotFound, + exceptions.NetworkNotFoundClient, + exceptions.PortNotFoundClient)): + return True + return (isinstance(ex, exceptions.NeutronClientException) and + ex.status_code == 404) + + def is_conflict(self, ex): + if not isinstance(ex, exceptions.NeutronClientException): + return False + return ex.status_code == 409 + + def is_over_limit(self, ex): + if not isinstance(ex, exceptions.NeutronClientException): + return False + return ex.status_code == 413 diff --git a/gbpautomation/heat/engine/resources/__init__.py b/gbpautomation/heat/engine/resources/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/gbpautomation/heat/engine/resources/neutron/__init__.py b/gbpautomation/heat/engine/resources/neutron/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/gbpautomation/heat/engine/resources/neutron/gbpresource.py b/gbpautomation/heat/engine/resources/neutron/gbpresource.py new file mode 100644 index 0000000..fb0b1ff --- /dev/null +++ b/gbpautomation/heat/engine/resources/neutron/gbpresource.py @@ -0,0 +1,151 @@ +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +from heat.common import exception +from heat.engine import resource +from heat.engine import scheduler +from heat.openstack.common import log as logging + +LOG = logging.getLogger(__name__) + + +class GBPResource(resource.Resource): + + default_client_name = 'grouppolicy' + + def grouppolicy(self): + return self.client('grouppolicy') + + def validate(self): + ''' + Validate any of the provided params + ''' + res = super(GBPResource, self).validate() + if res: + return res + return self.validate_properties(self.properties) + + @staticmethod + def validate_properties(properties): + ''' + Validates to ensure nothing in value_specs overwrites + any key that exists in the schema. + + Also ensures that shared and tenant_id is not specified + in value_specs. + ''' + if 'value_specs' in properties.keys(): + vs = properties.get('value_specs') + banned_keys = set(['shared', 'tenant_id']).union( + properties.keys()) + for k in banned_keys.intersection(vs.keys()): + return '%s not allowed in value_specs' % k + + @staticmethod + def _validate_depr_property_required(properties, prop_key, depr_prop_key): + prop_value = properties.get(prop_key) + depr_prop_value = properties.get(depr_prop_key) + + if prop_value and depr_prop_value: + raise exception.ResourcePropertyConflict(prop_key, + depr_prop_key) + if not prop_value and not depr_prop_value: + msg = _('Either %(prop_key)s or %(depr_prop_key)s' + ' should be specified.' + ) % {'prop_key': prop_key, + 'depr_prop_key': depr_prop_key} + raise exception.StackValidationFailed(message=msg) + + @staticmethod + def prepare_properties(properties, name): + ''' + Prepares the property values so that they can be passed directly to + the Neutron create call. + + Removes None values and value_specs, merges value_specs with the main + values. + ''' + props = dict((k, v) for k, v in properties.items() + if v is not None and k != 'value_specs') + + if 'name' in properties.keys(): + props.setdefault('name', name) + + if 'value_specs' in properties.keys(): + props.update(properties.get('value_specs')) + + return props + + def prepare_update_properties(self, definition): + ''' + Prepares the property values so that they can be passed directly to + the Neutron update call. + + Removes any properties which are not update_allowed, then processes + as for prepare_properties. + ''' + p = definition.properties(self.properties_schema, self.context) + update_props = dict((k, v) for k, v in p.items() + if p.props.get(k).schema.update_allowed) + + props = self.prepare_properties( + update_props, + self.physical_resource_name()) + return props + + @staticmethod + def is_built(attributes): + status = attributes['status'] + if status == 'BUILD': + return False + if status in ('ACTIVE', 'DOWN'): + return True + elif status == 'ERROR': + raise resource.ResourceInError( + resource_status=status) + else: + raise resource.ResourceUnknownStatus( + resource_status=status, + result=_('Resource is not built')) + + def _resolve_attribute(self, name): + try: + attributes = self._show_resource() + except Exception as ex: + self.client_plugin().ignore_not_found(ex) + return None + if name == 'show': + return attributes + + return attributes[name] + + def _confirm_delete(self): + while True: + try: + yield + self._show_resource() + except Exception as ex: + self.client_plugin().ignore_not_found(ex) + return + + def FnGetRefId(self): + return unicode(self.resource_id) + + def _delete_task(self): + delete_task = scheduler.TaskRunner(self._confirm_delete) + delete_task.start() + return delete_task + + def check_delete_complete(self, delete_task): + # if the resource was already deleted, delete_task will be None + return delete_task is None or delete_task.step() diff --git a/gbpautomation/heat/engine/resources/neutron/grouppolicy.py b/gbpautomation/heat/engine/resources/neutron/grouppolicy.py new file mode 100644 index 0000000..03608d8 --- /dev/null +++ b/gbpautomation/heat/engine/resources/neutron/grouppolicy.py @@ -0,0 +1,357 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +from gbpautomation.heat.engine.resources.neutron import gbpresource +from neutronclient.common.exceptions import NeutronClientException + +from heat.engine import attributes +from heat.engine import properties + + +class Endpoint(gbpresource.GBPResource): + + PROPERTIES = ( + TENANT_ID, NAME, DESCRIPTION, ENDPOINT_GROUP_ID + ) = ( + 'tenant_id', 'name', 'description', 'endpoint_group_id' + ) + + ATTRIBUTES = ( + NEUTRON_PORT_ID + ) = ( + 'neutron_port_id' + ) + + properties_schema = { + TENANT_ID: properties.Schema( + properties.Schema.STRING, + _('Tenant id of the endpoint.') + ), + NAME: properties.Schema( + properties.Schema.STRING, + _('Name of the endpoint.'), + update_allowed=True + ), + DESCRIPTION: properties.Schema( + properties.Schema.STRING, + _('Description of the endpoint.'), + update_allowed=True + ), + ENDPOINT_GROUP_ID: properties.Schema( + properties.Schema.STRING, + _('Endpoint group id of the endpoint.'), + required=True, + update_allowed=True + ) + } + + attributes_schema = { + NEUTRON_PORT_ID: attributes.Schema( + _("Neutron port id of this endpoint") + ) + } + + def _show_resource(self): + client = self.grouppolicy() + ep_id = self.resource_id + return client.show_endpoint(ep_id)['endpoint'] + + def handle_create(self): + client = self.grouppolicy() + + props = {} + for key in self.properties: + if self.properties.get(key) is not None: + props[key] = self.properties.get(key) + + ep = client.create_endpoint({'endpoint': props})['endpoint'] + + self.resource_id_set(ep['id']) + + def _resolve_attribute(self, name): + client = self.grouppolicy() + ep_id = self.resource_id + if name == 'neutron_port_id': + return client.show_endpoint(ep_id)['endpoint']['neutron_port_id'] + return super(Endpoint, self)._resolve_attribute(name) + + def handle_delete(self): + + client = self.grouppolicy() + ep_id = self.resource_id + + try: + client.delete_endpoint(ep_id) + except NeutronClientException as ex: + self.client_plugin().ignore_not_found(ex) + else: + return self._delete_task() + + def handle_update(self, json_snippet, tmpl_diff, prop_diff): + if prop_diff: + self.grouppolicy().update_endpoint( + self.resource_id, {'endpoint': prop_diff}) + + +class EndpointGroup(gbpresource.GBPResource): + + PROPERTIES = ( + TENANT_ID, NAME, DESCRIPTION, L2_POLICY_ID, + PROVIDED_CONTRACTS, CONSUMED_CONTRACTS + ) = ( + 'tenant_id', 'name', 'description', 'l2_policy_id', + 'provided_contracts', 'consumed_contracts' + ) + + properties_schema = { + TENANT_ID: properties.Schema( + properties.Schema.STRING, + _('Tenant id of the endpoint group.') + ), + NAME: properties.Schema( + properties.Schema.STRING, + _('Name of the endpoint group.'), + update_allowed=True + ), + DESCRIPTION: properties.Schema( + properties.Schema.STRING, + _('Description of the endpoint group.'), + update_allowed=True + ), + L2_POLICY_ID: properties.Schema( + properties.Schema.STRING, + _('L2 policy id of the endpoint group.'), + update_allowed=True + ), + PROVIDED_CONTRACTS: properties.Schema( + properties.Schema.LIST, + _('Provided contracts for the endpoint group.'), + update_allowed=True + ), + CONSUMED_CONTRACTS: properties.Schema( + properties.Schema.LIST, + _('Consumed contracts for the endpoint group.'), + update_allowed=True + ) + } + + def _show_resource(self): + client = self.grouppolicy() + epg_id = self.resource_id + return client.show_endpoint_group(epg_id)['endpoint_group'] + + def handle_create(self): + client = self.grouppolicy() + + props = {} + for key in self.properties: + if self.properties.get(key) is not None: + props[key] = self.properties.get(key) + + provided_contracts_list = {} + consumed_contracts_list = {} + props_provided_contracts = props.get('provided_contracts', []) + props_consumed_contracts = props.get('consumed_contracts', []) + + for prop_prov_contract in props_provided_contracts: + contract_id = prop_prov_contract['contract_id'] + contract_scope = prop_prov_contract['contract_scope'] + provided_contracts_list.update({contract_id: contract_scope}) + + for prop_cons_contract in props_consumed_contracts: + contract_id = prop_cons_contract['contract_id'] + contract_scope = prop_cons_contract['contract_scope'] + consumed_contracts_list.update({contract_id: contract_scope}) + + if provided_contracts_list: + props['provided_contracts'] = provided_contracts_list + if consumed_contracts_list: + props['consumed_contracts'] = consumed_contracts_list + + epg = client.create_endpoint_group( + {'endpoint_group': props})['endpoint_group'] + + self.resource_id_set(epg['id']) + + def handle_delete(self): + + client = self.grouppolicy() + epg_id = self.resource_id + + try: + client.delete_endpoint_group(epg_id) + except NeutronClientException as ex: + self.client_plugin().ignore_not_found(ex) + else: + return self._delete_task() + + def handle_update(self, json_snippet, tmpl_diff, prop_diff): + if prop_diff: + self.grouppolicy().update_endpoint_group( + self.resource_id, {'endpoint_group': prop_diff}) + + +class L2Policy(gbpresource.GBPResource): + + PROPERTIES = ( + TENANT_ID, NAME, DESCRIPTION, L3_POLICY_ID + ) = ( + 'tenant_id', 'name', 'description', 'l3_policy_id' + ) + + properties_schema = { + TENANT_ID: properties.Schema( + properties.Schema.STRING, + _('Tenant id of the L2 policy.') + ), + NAME: properties.Schema( + properties.Schema.STRING, + _('Name of the L2 policy.'), + update_allowed=True + ), + DESCRIPTION: properties.Schema( + properties.Schema.STRING, + _('Description of the L2 policy.'), + update_allowed=True + ), + L3_POLICY_ID: properties.Schema( + properties.Schema.STRING, + _('L3 policy id associated with l2 policy.'), + required=True, + update_allowed=True + ) + } + + def _show_resource(self): + client = self.grouppolicy() + l2_policy_id = self.resource_id + return client.show_l2_policy(l2_policy_id)['l2_policy'] + + def handle_create(self): + client = self.grouppolicy() + + props = {} + for key in self.properties: + if self.properties.get(key) is not None: + props[key] = self.properties.get(key) + + l2_policy = client.create_l2_policy( + {'l2_policy': props})['l2_policy'] + + self.resource_id_set(l2_policy['id']) + + def handle_delete(self): + + client = self.grouppolicy() + l2_policy_id = self.resource_id + + try: + client.delete_l2_policy(l2_policy_id) + except NeutronClientException as ex: + self.client_plugin().ignore_not_found(ex) + else: + return self._delete_task() + + def handle_update(self, json_snippet, tmpl_diff, prop_diff): + if prop_diff: + self.grouppolicy().update_l2_policy( + self.resource_id, {'l2_policy': prop_diff}) + + +class L3Policy(gbpresource.GBPResource): + + PROPERTIES = ( + TENANT_ID, NAME, DESCRIPTION, IP_VERSION, IP_POOL, + SUBNET_PREFIX_LENGTH + ) = ( + 'tenant_id', 'name', 'description', 'ip_version', 'ip_pool', + 'subnet_prefix_length' + ) + + properties_schema = { + TENANT_ID: properties.Schema( + properties.Schema.STRING, + _('Tenant id of the L3 policy.') + ), + NAME: properties.Schema( + properties.Schema.STRING, + _('Name of the L3 policy.'), + update_allowed=True + ), + DESCRIPTION: properties.Schema( + properties.Schema.STRING, + _('Description of the L3 policy.'), + update_allowed=True + ), + IP_VERSION: properties.Schema( + properties.Schema.STRING, + _('IP version of the L3 policy.'), + update_allowed=False + ), + IP_POOL: properties.Schema( + properties.Schema.STRING, + _('IP pool of the L3 policy.'), + update_allowed=False + ), + SUBNET_PREFIX_LENGTH: properties.Schema( + properties.Schema.INTEGER, + _('Subnet prefix length of L3 policy.'), + update_allowed=True + ) + } + + def _show_resource(self): + client = self.grouppolicy() + l3_policy_id = self.resource_id + return client.show_l3_policy(l3_policy_id)['l3_policy'] + + def handle_create(self): + client = self.grouppolicy() + + props = {} + for key in self.properties: + if self.properties.get(key) is not None: + props[key] = self.properties.get(key) + + l3_policy = client.create_l3_policy( + {'l3_policy': props})['l3_policy'] + + self.resource_id_set(l3_policy['id']) + + def handle_delete(self): + + client = self.grouppolicy() + l3_policy_id = self.resource_id + + try: + client.delete_l3_policy(l3_policy_id) + except NeutronClientException as ex: + self.client_plugin().ignore_not_found(ex) + else: + return self._delete_task() + + def handle_update(self, json_snippet, tmpl_diff, prop_diff): + if prop_diff: + self.grouppolicy().update_l3_policy( + self.resource_id, {'l3_policy': prop_diff}) + + +def resource_mapping(): + return { + 'OS::Neutron::Endpoint': Endpoint, + 'OS::Neutron::EndpointGroup': EndpointGroup, + 'OS::Neutron::L2Policy': L2Policy, + 'OS::Neutron::L3Policy': L3Policy, + } diff --git a/gbpautomation/heat/tests/test_grouppolicy.py b/gbpautomation/heat/tests/test_grouppolicy.py new file mode 100644 index 0000000..d96e362 --- /dev/null +++ b/gbpautomation/heat/tests/test_grouppolicy.py @@ -0,0 +1,611 @@ +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +import copy + +from gbpautomation.heat.engine.resources.neutron import grouppolicy +from gbpclient.v2_0 import client as gbpclient +from heat.common import exception +from heat.common import template_format +from heat.tests.common import HeatTestCase + +from heat.engine import scheduler +from heat.tests import utils + + +endpoint_template = ''' +{ + "AWSTemplateFormatVersion" : "2010-09-09", + "Description" : "Template to test neutron endpoint resource", + "Parameters" : {}, + "Resources" : { + "endpoint": { + "Type": "OS::Neutron::Endpoint", + "Properties": { + "name": "test-endpoint", + "endpoint_group_id": "epg-id", + "description": "test endpoint resource" + } + } + } +} +''' + +endpoint_group_template = ''' +{ + "AWSTemplateFormatVersion" : "2010-09-09", + "Description" : "Template to test neutron endpoint group resource", + "Parameters" : {}, + "Resources" : { + "endpoint_group": { + "Type": "OS::Neutron::EndpointGroup", + "Properties": { + "name": "test-endpoint-group", + "description": "test endpoint group resource", + "l2_policy_id": "l2-policy-id", + "provided_contracts": [ + {"contract_id": "contract1", "contract_scope": "scope1"}, + {"contract_id": "contract2", "contract_scope": "scope2"} + ], + "consumed_contracts": [ + {"contract_id": "contract3", "contract_scope": "scope3"}, + {"contract_id": "contract4", "contract_scope": "scope4"} + ] + } + } + } +} +''' + +l2_policy_template = ''' +{ + "AWSTemplateFormatVersion" : "2010-09-09", + "Description" : "Template to test neutron l2 policy", + "Parameters" : {}, + "Resources" : { + "l2_policy": { + "Type": "OS::Neutron::L2Policy", + "Properties": { + "name": "test-l2-policy", + "description": "test L2 policy resource", + "l3_policy_id": "l3-policy-id" + } + } + } +} +''' + +l3_policy_template = ''' +{ + "AWSTemplateFormatVersion" : "2010-09-09", + "Description" : "Template to test neutron l3 policy", + "Parameters" : {}, + "Resources" : { + "l3_policy": { + "Type": "OS::Neutron::L3Policy", + "Properties": { + "name": "test-l3-policy", + "description": "test L3 policy resource", + "ip_version": "4", + "ip_pool": "10.20.20.0", + "subnet_prefix_length": 24 + } + } + } +} +''' + + +class EndpointTest(HeatTestCase): + + def setUp(self): + super(EndpointTest, self).setUp() + self.m.StubOutWithMock(gbpclient.Client, 'create_endpoint') + self.m.StubOutWithMock(gbpclient.Client, 'delete_endpoint') + self.m.StubOutWithMock(gbpclient.Client, 'show_endpoint') + self.m.StubOutWithMock(gbpclient.Client, 'update_endpoint') + self.stub_keystoneclient() + + def create_endpoint(self): + gbpclient.Client.create_endpoint({ + 'endpoint': { + 'name': 'test-endpoint', + 'endpoint_group_id': 'epg-id', + "description": "test endpoint resource" + } + }).AndReturn({'endpoint': {'id': '5678'}}) + + snippet = template_format.parse(endpoint_template) + stack = utils.parse_stack(snippet) + resource_defns = stack.t.resource_definitions(stack) + return grouppolicy.Endpoint( + 'endpoint', resource_defns['endpoint'], stack) + + def test_create(self): + rsrc = self.create_endpoint() + self.m.ReplayAll() + scheduler.TaskRunner(rsrc.create)() + self.assertEqual((rsrc.CREATE, rsrc.COMPLETE), rsrc.state) + self.m.VerifyAll() + + def test_create_failed(self): + gbpclient.Client.create_endpoint({ + 'endpoint': { + 'name': 'test-endpoint', + 'endpoint_group_id': 'epg-id', + "description": "test endpoint resource" + } + }).AndRaise(grouppolicy.NeutronClientException()) + self.m.ReplayAll() + + snippet = template_format.parse(endpoint_template) + stack = utils.parse_stack(snippet) + resource_defns = stack.t.resource_definitions(stack) + rsrc = grouppolicy.Endpoint( + 'endpoint', resource_defns['endpoint'], stack) + + error = self.assertRaises(exception.ResourceFailure, + scheduler.TaskRunner(rsrc.create)) + self.assertEqual( + 'NeutronClientException: An unknown exception occurred.', + str(error)) + self.assertEqual((rsrc.CREATE, rsrc.FAILED), rsrc.state) + self.m.VerifyAll() + + def test_delete(self): + gbpclient.Client.delete_endpoint('5678') + gbpclient.Client.show_endpoint('5678').AndRaise( + grouppolicy.NeutronClientException(status_code=404)) + + rsrc = self.create_endpoint() + self.m.ReplayAll() + scheduler.TaskRunner(rsrc.create)() + scheduler.TaskRunner(rsrc.delete)() + self.assertEqual((rsrc.DELETE, rsrc.COMPLETE), rsrc.state) + self.m.VerifyAll() + + def test_delete_already_gone(self): + gbpclient.Client.delete_endpoint('5678').AndRaise( + grouppolicy.NeutronClientException(status_code=404)) + + rsrc = self.create_endpoint() + self.m.ReplayAll() + scheduler.TaskRunner(rsrc.create)() + scheduler.TaskRunner(rsrc.delete)() + self.assertEqual((rsrc.DELETE, rsrc.COMPLETE), rsrc.state) + self.m.VerifyAll() + + def test_delete_failed(self): + gbpclient.Client.delete_endpoint('5678').AndRaise( + grouppolicy.NeutronClientException(status_code=400)) + + rsrc = self.create_endpoint() + self.m.ReplayAll() + scheduler.TaskRunner(rsrc.create)() + error = self.assertRaises(exception.ResourceFailure, + scheduler.TaskRunner(rsrc.delete)) + self.assertEqual( + 'NeutronClientException: An unknown exception occurred.', + str(error)) + self.assertEqual((rsrc.DELETE, rsrc.FAILED), rsrc.state) + self.m.VerifyAll() + + def test_attribute(self): + rsrc = self.create_endpoint() + gbpclient.Client.show_endpoint('5678').MultipleTimes( + ).AndReturn( + {'endpoint': {'neutron_port_id': '1234'}}) + self.m.ReplayAll() + scheduler.TaskRunner(rsrc.create)() + self.assertEqual('1234', rsrc.FnGetAtt('neutron_port_id')) + self.m.VerifyAll() + + def test_attribute_failed(self): + rsrc = self.create_endpoint() + self.m.ReplayAll() + scheduler.TaskRunner(rsrc.create)() + error = self.assertRaises(exception.InvalidTemplateAttribute, + rsrc.FnGetAtt, 'l2_policy_id') + self.assertEqual( + 'The Referenced Attribute (endpoint l2_policy_id) is ' + 'incorrect.', str(error)) + self.m.VerifyAll() + + def test_update(self): + rsrc = self.create_endpoint() + gbpclient.Client.update_endpoint( + '5678', {'endpoint': {'endpoint_group_id': 'epg_id_update'}}) + self.m.ReplayAll() + scheduler.TaskRunner(rsrc.create)() + + update_template = copy.deepcopy(rsrc.t) + update_template['Properties']['endpoint_group_id'] = 'epg_id_update' + scheduler.TaskRunner(rsrc.update, update_template)() + + self.m.VerifyAll() + + +class EndpointGroupTest(HeatTestCase): + + def setUp(self): + super(EndpointGroupTest, self).setUp() + self.m.StubOutWithMock(gbpclient.Client, 'create_endpoint_group') + self.m.StubOutWithMock(gbpclient.Client, 'delete_endpoint_group') + self.m.StubOutWithMock(gbpclient.Client, 'show_endpoint_group') + self.m.StubOutWithMock(gbpclient.Client, 'update_endpoint_group') + self.stub_keystoneclient() + + def create_endpoint_group(self): + gbpclient.Client.create_endpoint_group({ + "endpoint_group": { + "name": "test-endpoint-group", + "description": "test endpoint group resource", + "l2_policy_id": "l2-policy-id", + "provided_contracts": { + "contract1": "scope1", + "contract2": "scope2" + }, + "consumed_contracts": { + "contract3": "scope3", + "contract4": "scope4" + } + } + }).AndReturn({'endpoint_group': {'id': '5678'}}) + + snippet = template_format.parse(endpoint_group_template) + stack = utils.parse_stack(snippet) + resource_defns = stack.t.resource_definitions(stack) + return grouppolicy.EndpointGroup( + 'endpoint_group', resource_defns['endpoint_group'], stack) + + def test_create(self): + rsrc = self.create_endpoint_group() + self.m.ReplayAll() + scheduler.TaskRunner(rsrc.create)() + self.assertEqual((rsrc.CREATE, rsrc.COMPLETE), rsrc.state) + self.m.VerifyAll() + + def test_create_failed(self): + gbpclient.Client.create_endpoint_group({ + "endpoint_group": { + "name": "test-endpoint-group", + "description": "test endpoint group resource", + "l2_policy_id": "l2-policy-id", + "provided_contracts": { + "contract1": "scope1", + "contract2": "scope2" + }, + "consumed_contracts": { + "contract3": "scope3", + "contract4": "scope4" + } + } + }).AndRaise(grouppolicy.NeutronClientException()) + self.m.ReplayAll() + + snippet = template_format.parse(endpoint_group_template) + stack = utils.parse_stack(snippet) + resource_defns = stack.t.resource_definitions(stack) + rsrc = grouppolicy.EndpointGroup( + 'endpoint_group', resource_defns['endpoint_group'], stack) + + error = self.assertRaises(exception.ResourceFailure, + scheduler.TaskRunner(rsrc.create)) + self.assertEqual( + 'NeutronClientException: An unknown exception occurred.', + str(error)) + self.assertEqual((rsrc.CREATE, rsrc.FAILED), rsrc.state) + self.m.VerifyAll() + + def test_delete(self): + gbpclient.Client.delete_endpoint_group('5678') + gbpclient.Client.show_endpoint_group('5678').AndRaise( + grouppolicy.NeutronClientException(status_code=404)) + + rsrc = self.create_endpoint_group() + self.m.ReplayAll() + scheduler.TaskRunner(rsrc.create)() + scheduler.TaskRunner(rsrc.delete)() + self.assertEqual((rsrc.DELETE, rsrc.COMPLETE), rsrc.state) + self.m.VerifyAll() + + def test_delete_already_gone(self): + gbpclient.Client.delete_endpoint_group('5678').AndRaise( + grouppolicy.NeutronClientException(status_code=404)) + + rsrc = self.create_endpoint_group() + self.m.ReplayAll() + scheduler.TaskRunner(rsrc.create)() + scheduler.TaskRunner(rsrc.delete)() + self.assertEqual((rsrc.DELETE, rsrc.COMPLETE), rsrc.state) + self.m.VerifyAll() + + def test_delete_failed(self): + gbpclient.Client.delete_endpoint_group('5678').AndRaise( + grouppolicy.NeutronClientException(status_code=400)) + + rsrc = self.create_endpoint_group() + self.m.ReplayAll() + scheduler.TaskRunner(rsrc.create)() + error = self.assertRaises(exception.ResourceFailure, + scheduler.TaskRunner(rsrc.delete)) + self.assertEqual( + 'NeutronClientException: An unknown exception occurred.', + str(error)) + self.assertEqual((rsrc.DELETE, rsrc.FAILED), rsrc.state) + self.m.VerifyAll() + + def test_attribute_failed(self): + rsrc = self.create_endpoint_group() + self.m.ReplayAll() + scheduler.TaskRunner(rsrc.create)() + error = self.assertRaises(exception.InvalidTemplateAttribute, + rsrc.FnGetAtt, 'l3_policy_id') + self.assertEqual( + 'The Referenced Attribute (endpoint_group l3_policy_id) is ' + 'incorrect.', str(error)) + self.m.VerifyAll() + + def test_update(self): + rsrc = self.create_endpoint_group() + gbpclient.Client.update_endpoint_group( + '5678', {'endpoint_group': {'l2_policy_id': 'l2_id_update'}}) + self.m.ReplayAll() + scheduler.TaskRunner(rsrc.create)() + + update_template = copy.deepcopy(rsrc.t) + update_template['Properties']['l2_policy_id'] = 'l2_id_update' + scheduler.TaskRunner(rsrc.update, update_template)() + + self.m.VerifyAll() + + +class L2PolicyTest(HeatTestCase): + + def setUp(self): + super(L2PolicyTest, self).setUp() + self.m.StubOutWithMock(gbpclient.Client, 'create_l2_policy') + self.m.StubOutWithMock(gbpclient.Client, 'delete_l2_policy') + self.m.StubOutWithMock(gbpclient.Client, 'show_l2_policy') + self.m.StubOutWithMock(gbpclient.Client, 'update_l2_policy') + self.stub_keystoneclient() + + def create_l2_policy(self): + gbpclient.Client.create_l2_policy({ + 'l2_policy': { + "name": "test-l2-policy", + "description": "test L2 policy resource", + "l3_policy_id": "l3-policy-id" + } + }).AndReturn({'l2_policy': {'id': '5678'}}) + + snippet = template_format.parse(l2_policy_template) + stack = utils.parse_stack(snippet) + resource_defns = stack.t.resource_definitions(stack) + return grouppolicy.L2Policy( + 'l2_policy', resource_defns['l2_policy'], stack) + + def test_create(self): + rsrc = self.create_l2_policy() + self.m.ReplayAll() + scheduler.TaskRunner(rsrc.create)() + self.assertEqual((rsrc.CREATE, rsrc.COMPLETE), rsrc.state) + self.m.VerifyAll() + + def test_create_failed(self): + gbpclient.Client.create_l2_policy({ + 'l2_policy': { + "name": "test-l2-policy", + "description": "test L2 policy resource", + "l3_policy_id": "l3-policy-id" + } + }).AndRaise(grouppolicy.NeutronClientException()) + self.m.ReplayAll() + + snippet = template_format.parse(l2_policy_template) + stack = utils.parse_stack(snippet) + resource_defns = stack.t.resource_definitions(stack) + rsrc = grouppolicy.L2Policy( + 'l2_policy', resource_defns['l2_policy'], stack) + + error = self.assertRaises(exception.ResourceFailure, + scheduler.TaskRunner(rsrc.create)) + self.assertEqual( + 'NeutronClientException: An unknown exception occurred.', + str(error)) + self.assertEqual((rsrc.CREATE, rsrc.FAILED), rsrc.state) + self.m.VerifyAll() + + def test_delete(self): + gbpclient.Client.delete_l2_policy('5678') + gbpclient.Client.show_l2_policy('5678').AndRaise( + grouppolicy.NeutronClientException(status_code=404)) + + rsrc = self.create_l2_policy() + self.m.ReplayAll() + scheduler.TaskRunner(rsrc.create)() + scheduler.TaskRunner(rsrc.delete)() + self.assertEqual((rsrc.DELETE, rsrc.COMPLETE), rsrc.state) + self.m.VerifyAll() + + def test_delete_already_gone(self): + gbpclient.Client.delete_l2_policy('5678').AndRaise( + grouppolicy.NeutronClientException(status_code=404)) + + rsrc = self.create_l2_policy() + self.m.ReplayAll() + scheduler.TaskRunner(rsrc.create)() + scheduler.TaskRunner(rsrc.delete)() + self.assertEqual((rsrc.DELETE, rsrc.COMPLETE), rsrc.state) + self.m.VerifyAll() + + def test_delete_failed(self): + gbpclient.Client.delete_l2_policy('5678').AndRaise( + grouppolicy.NeutronClientException(status_code=400)) + + rsrc = self.create_l2_policy() + self.m.ReplayAll() + scheduler.TaskRunner(rsrc.create)() + error = self.assertRaises(exception.ResourceFailure, + scheduler.TaskRunner(rsrc.delete)) + self.assertEqual( + 'NeutronClientException: An unknown exception occurred.', + str(error)) + self.assertEqual((rsrc.DELETE, rsrc.FAILED), rsrc.state) + self.m.VerifyAll() + + def test_attribute_failed(self): + rsrc = self.create_l2_policy() + self.m.ReplayAll() + scheduler.TaskRunner(rsrc.create)() + error = self.assertRaises(exception.InvalidTemplateAttribute, + rsrc.FnGetAtt, 'endpoint_id') + self.assertEqual( + 'The Referenced Attribute (l2_policy endpoint_id) is ' + 'incorrect.', str(error)) + self.m.VerifyAll() + + def test_update(self): + rsrc = self.create_l2_policy() + gbpclient.Client.update_l2_policy( + '5678', {'l2_policy': {'l3_policy_id': 'l3_id_update'}}) + self.m.ReplayAll() + scheduler.TaskRunner(rsrc.create)() + + update_template = copy.deepcopy(rsrc.t) + update_template['Properties']['l3_policy_id'] = 'l3_id_update' + scheduler.TaskRunner(rsrc.update, update_template)() + + self.m.VerifyAll() + + +class L3PolicyTest(HeatTestCase): + + def setUp(self): + super(L3PolicyTest, self).setUp() + self.m.StubOutWithMock(gbpclient.Client, 'create_l3_policy') + self.m.StubOutWithMock(gbpclient.Client, 'delete_l3_policy') + self.m.StubOutWithMock(gbpclient.Client, 'show_l3_policy') + self.m.StubOutWithMock(gbpclient.Client, 'update_l3_policy') + self.stub_keystoneclient() + + def create_l3_policy(self): + gbpclient.Client.create_l3_policy({ + 'l3_policy': { + "name": "test-l3-policy", + "description": "test L3 policy resource", + "ip_version": "4", + "ip_pool": "10.20.20.0", + "subnet_prefix_length": 24 + } + }).AndReturn({'l3_policy': {'id': '5678'}}) + + snippet = template_format.parse(l3_policy_template) + stack = utils.parse_stack(snippet) + resource_defns = stack.t.resource_definitions(stack) + return grouppolicy.L3Policy( + 'l3_policy', resource_defns['l3_policy'], stack) + + def test_create(self): + rsrc = self.create_l3_policy() + self.m.ReplayAll() + scheduler.TaskRunner(rsrc.create)() + self.assertEqual((rsrc.CREATE, rsrc.COMPLETE), rsrc.state) + self.m.VerifyAll() + + def test_create_failed(self): + gbpclient.Client.create_l3_policy({ + 'l3_policy': { + "name": "test-l3-policy", + "description": "test L3 policy resource", + "ip_version": "4", + "ip_pool": "10.20.20.0", + "subnet_prefix_length": 24 + } + }).AndRaise(grouppolicy.NeutronClientException()) + self.m.ReplayAll() + + snippet = template_format.parse(l3_policy_template) + stack = utils.parse_stack(snippet) + resource_defns = stack.t.resource_definitions(stack) + rsrc = grouppolicy.L3Policy( + 'l3_policy', resource_defns['l3_policy'], stack) + + error = self.assertRaises(exception.ResourceFailure, + scheduler.TaskRunner(rsrc.create)) + self.assertEqual( + 'NeutronClientException: An unknown exception occurred.', + str(error)) + self.assertEqual((rsrc.CREATE, rsrc.FAILED), rsrc.state) + self.m.VerifyAll() + + def test_delete(self): + gbpclient.Client.delete_l3_policy('5678') + gbpclient.Client.show_l3_policy('5678').AndRaise( + grouppolicy.NeutronClientException(status_code=404)) + + rsrc = self.create_l3_policy() + self.m.ReplayAll() + scheduler.TaskRunner(rsrc.create)() + scheduler.TaskRunner(rsrc.delete)() + self.assertEqual((rsrc.DELETE, rsrc.COMPLETE), rsrc.state) + self.m.VerifyAll() + + def test_delete_already_gone(self): + gbpclient.Client.delete_l3_policy('5678').AndRaise( + grouppolicy.NeutronClientException(status_code=404)) + + rsrc = self.create_l3_policy() + self.m.ReplayAll() + scheduler.TaskRunner(rsrc.create)() + scheduler.TaskRunner(rsrc.delete)() + self.assertEqual((rsrc.DELETE, rsrc.COMPLETE), rsrc.state) + self.m.VerifyAll() + + def test_delete_failed(self): + gbpclient.Client.delete_l3_policy('5678').AndRaise( + grouppolicy.NeutronClientException(status_code=400)) + + rsrc = self.create_l3_policy() + self.m.ReplayAll() + scheduler.TaskRunner(rsrc.create)() + error = self.assertRaises(exception.ResourceFailure, + scheduler.TaskRunner(rsrc.delete)) + self.assertEqual( + 'NeutronClientException: An unknown exception occurred.', + str(error)) + self.assertEqual((rsrc.DELETE, rsrc.FAILED), rsrc.state) + self.m.VerifyAll() + + def test_attribute_failed(self): + rsrc = self.create_l3_policy() + self.m.ReplayAll() + scheduler.TaskRunner(rsrc.create)() + error = self.assertRaises(exception.InvalidTemplateAttribute, + rsrc.FnGetAtt, 'subnet_id') + self.assertEqual( + 'The Referenced Attribute (l3_policy subnet_id) is ' + 'incorrect.', str(error)) + self.m.VerifyAll() + + def test_update(self): + rsrc = self.create_l3_policy() + gbpclient.Client.update_l3_policy( + '5678', {'l3_policy': {'subnet_prefix_length': 28}}) + self.m.ReplayAll() + scheduler.TaskRunner(rsrc.create)() + + update_template = copy.deepcopy(rsrc.t) + update_template['Properties']['subnet_prefix_length'] = 28 + scheduler.TaskRunner(rsrc.update, update_template)() + + self.m.VerifyAll() diff --git a/setup.cfg b/setup.cfg index 28727cd..695e6be 100644 --- a/setup.cfg +++ b/setup.cfg @@ -44,4 +44,8 @@ input_file = gbpautomation/locale/group-based-policy-automation.pot [extract_messages] keywords = _ gettext ngettext l_ lazy_gettext mapping_file = babel.cfg -output_file = gbpautomation/locale/group-based-policy-automation.pot \ No newline at end of file +output_file = gbpautomation/locale/group-based-policy-automation.pot + +[entry_points] +heat.clients = + grouppolicy = gbpautomation.heat.engine.clients.os.grouppolicy:GBPClientPlugin diff --git a/test-requirements.txt b/test-requirements.txt index 71095ed..3491b0c 100644 --- a/test-requirements.txt +++ b/test-requirements.txt @@ -3,12 +3,14 @@ # process, which may cause wedges in the gate later. -e git://github.com/openstack/heat.git@stable/juno#egg=heat +-e git://github.com/stackforge/python-group-based-policy-client.git@master#egg=gbpclient # Hacking already pins down pep8, pyflakes and flake8 hacking>=0.8.0,<0.9 coverage>=3.6 discover lockfile>=0.8 mock>=1.0 +python-subunit>=0.0.18 mox>=0.5.3 MySQL-python oslosphinx>=2.2.0 # Apache-2.0