From ca098b6bc56e8073890b4d00223e5acd6dca5288 Mon Sep 17 00:00:00 2001 From: Hemanth Ravi Date: Mon, 20 Oct 2014 12:37:41 -0700 Subject: [PATCH] Group Policy API-3 HEAT resources: Contracts This is the third patch in the Group Policy resources implementation series. This patch implements: Contracts In the context of larger Group Policy model, Contracts contain Policy Rules and Endpoint Groups consume or provide Contracts. Change-Id: I9d92913f656af5a5d177f0cff5f3b306ced06fdf Implements: blueprint group-based-policy-automation --- .../engine/resources/neutron/grouppolicy.py | 91 ++++++++++- gbpautomation/heat/tests/test_grouppolicy.py | 146 +++++++++++++++++- 2 files changed, 224 insertions(+), 13 deletions(-) diff --git a/gbpautomation/heat/engine/resources/neutron/grouppolicy.py b/gbpautomation/heat/engine/resources/neutron/grouppolicy.py index 8f3e7f8..610931d 100644 --- a/gbpautomation/heat/engine/resources/neutron/grouppolicy.py +++ b/gbpautomation/heat/engine/resources/neutron/grouppolicy.py @@ -30,9 +30,9 @@ class Endpoint(gbpresource.GBPResource): ) ATTRIBUTES = ( - NEUTRON_PORT_ID + PORT_ID ) = ( - 'neutron_port_id' + 'port_id' ) properties_schema = { @@ -59,7 +59,7 @@ class Endpoint(gbpresource.GBPResource): } attributes_schema = { - NEUTRON_PORT_ID: attributes.Schema( + PORT_ID: attributes.Schema( _("Neutron port id of this endpoint") ) } @@ -84,8 +84,8 @@ class Endpoint(gbpresource.GBPResource): 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'] + if name == 'port_id': + return client.show_endpoint(ep_id)['endpoint']['port_id'] return super(Endpoint, self)._resolve_attribute(name) def handle_delete(self): @@ -585,6 +585,84 @@ class PolicyRule(gbpresource.GBPResource): self.resource_id, {'policy_rule': prop_diff}) +class Contract(gbpresource.GBPResource): + + PROPERTIES = ( + TENANT_ID, NAME, DESCRIPTION, PARENT_ID, CHILD_CONTRACTS, + POLICY_RULES + ) = ( + 'tenant_id', 'name', 'description', 'parent_id', 'child_contracts', + 'policy_rules' + ) + + properties_schema = { + TENANT_ID: properties.Schema( + properties.Schema.STRING, + _('Tenant id of the contract.') + ), + NAME: properties.Schema( + properties.Schema.STRING, + _('Name of the contract.'), + update_allowed=True + ), + DESCRIPTION: properties.Schema( + properties.Schema.STRING, + _('Description of the contract.'), + update_allowed=True + ), + PARENT_ID: properties.Schema( + properties.Schema.STRING, + _('Parent id of the contract.'), + update_allowed=False + ), + CHILD_CONTRACTS: properties.Schema( + properties.Schema.LIST, + _('List of child contracts.'), + default=None, update_allowed=True + ), + POLICY_RULES: properties.Schema( + properties.Schema.LIST, + _('List of policy rules.'), + default=None, update_allowed=True + ) + } + + def _show_resource(self): + client = self.grouppolicy() + contract_id = self.resource_id + return client.show_contract(contract_id)['contract'] + + 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) + + contract = client.create_contract( + {'contract': props})['contract'] + + self.resource_id_set(contract['id']) + + def handle_delete(self): + + client = self.grouppolicy() + contract_id = self.resource_id + + try: + client.delete_contract(contract_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_contract( + self.resource_id, {'contract': prop_diff}) + + def resource_mapping(): return { 'OS::Neutron::Endpoint': Endpoint, @@ -593,5 +671,6 @@ def resource_mapping(): 'OS::Neutron::L3Policy': L3Policy, 'OS::Neutron::PolicyClassifier': PolicyClassifier, 'OS::Neutron::PolicyAction': PolicyAction, - 'OS::Neutron::PolicyRule': PolicyRule + 'OS::Neutron::PolicyRule': PolicyRule, + 'OS::Neutron::Contract': Contract } diff --git a/gbpautomation/heat/tests/test_grouppolicy.py b/gbpautomation/heat/tests/test_grouppolicy.py index bd68f60..8dc7fd3 100644 --- a/gbpautomation/heat/tests/test_grouppolicy.py +++ b/gbpautomation/heat/tests/test_grouppolicy.py @@ -119,8 +119,8 @@ policy_classifier_template = ''' "protocol": "tcp", "port_range": "8000-9000", "direction": "bi" - } - } + } + } } } ''' @@ -138,8 +138,8 @@ policy_action_template = ''' "description": "test policy action resource", "action_type": "redirect", "action_value": "7890" - } - } + } + } } } ''' @@ -147,7 +147,7 @@ policy_action_template = ''' policy_rule_template = ''' { "AWSTemplateFormatVersion" : "2010-09-09", - "Description" : "Template to test neutron l3 policy", + "Description" : "Template to test neutron policy rule", "Parameters" : {}, "Resources" : { "policy_rule": { @@ -164,6 +164,26 @@ policy_rule_template = ''' } ''' +contract_template = ''' +{ + "AWSTemplateFormatVersion" : "2010-09-09", + "Description" : "Template to test contract", + "Parameters" : {}, + "Resources" : { + "contract": { + "Type": "OS::Neutron::Contract", + "Properties": { + "name": "test-contract", + "description": "test contract resource", + "parent_id": "3456", + "child_contracts": ["7890", "1234"], + "policy_rules": ["2345", "6789"] + } + } + } +} +''' + class EndpointTest(HeatTestCase): @@ -263,10 +283,10 @@ class EndpointTest(HeatTestCase): rsrc = self.create_endpoint() gbpclient.Client.show_endpoint('5678').MultipleTimes( ).AndReturn( - {'endpoint': {'neutron_port_id': '1234'}}) + {'endpoint': {'port_id': '1234'}}) self.m.ReplayAll() scheduler.TaskRunner(rsrc.create)() - self.assertEqual('1234', rsrc.FnGetAtt('neutron_port_id')) + self.assertEqual('1234', rsrc.FnGetAtt('port_id')) self.m.VerifyAll() def test_attribute_failed(self): @@ -974,3 +994,115 @@ class PolicyRuleTest(HeatTestCase): scheduler.TaskRunner(rsrc.update, update_template)() self.m.VerifyAll() + + +class ContractTest(HeatTestCase): + + def setUp(self): + super(ContractTest, self).setUp() + self.m.StubOutWithMock(gbpclient.Client, 'create_contract') + self.m.StubOutWithMock(gbpclient.Client, 'delete_contract') + self.m.StubOutWithMock(gbpclient.Client, 'show_contract') + self.m.StubOutWithMock(gbpclient.Client, 'update_contract') + self.stub_keystoneclient() + + def create_contract(self): + gbpclient.Client.create_contract({ + 'contract': { + "name": "test-contract", + "description": "test contract resource", + "parent_id": "3456", + "child_contracts": ["7890", "1234"], + "policy_rules": ["2345", "6789"] + } + }).AndReturn({'contract': {'id': '5678'}}) + + snippet = template_format.parse(contract_template) + stack = utils.parse_stack(snippet) + resource_defns = stack.t.resource_definitions(stack) + return grouppolicy.Contract( + 'contract', resource_defns['contract'], stack) + + def test_create(self): + rsrc = self.create_contract() + 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_contract({ + 'contract': { + "name": "test-contract", + "description": "test contract resource", + "parent_id": "3456", + "child_contracts": ["7890", "1234"], + "policy_rules": ["2345", "6789"] + } + }).AndRaise(grouppolicy.NeutronClientException()) + self.m.ReplayAll() + + snippet = template_format.parse(contract_template) + stack = utils.parse_stack(snippet) + resource_defns = stack.t.resource_definitions(stack) + rsrc = grouppolicy.Contract( + 'contract', resource_defns['contract'], 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_contract('5678') + gbpclient.Client.show_contract('5678').AndRaise( + grouppolicy.NeutronClientException(status_code=404)) + + rsrc = self.create_contract() + 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_contract('5678').AndRaise( + grouppolicy.NeutronClientException(status_code=404)) + + rsrc = self.create_contract() + 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_contract('5678').AndRaise( + grouppolicy.NeutronClientException(status_code=400)) + + rsrc = self.create_contract() + 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_update(self): + rsrc = self.create_contract() + gbpclient.Client.update_contract( + '5678', {'contract': {'child_contracts': ["1234"]}}) + self.m.ReplayAll() + scheduler.TaskRunner(rsrc.create)() + + update_template = copy.deepcopy(rsrc.t) + update_template['Properties']['child_contracts'] = ["1234"] + scheduler.TaskRunner(rsrc.update, update_template)() + + self.m.VerifyAll()