From 1d00e5af4d6bbdc8f4f40aa45ba3db53dccc23fd Mon Sep 17 00:00:00 2001 From: huangtianhua Date: Mon, 12 May 2014 17:30:56 +0800 Subject: [PATCH] Implement handle_update for FloatingIPAssociation resource 1. modify the property "port_id" to be required. 2. Implement handle_update for FloatingIPAssociation resource, allows to update FloatingIpAssociation with new port_id(and fixedIp if port has multipleIps) and new floatingip_id. Change-Id: Ie40669bee849d91ed93446dcf7f066fe6a5b2961 Implements: blueprint handle-update-for-floatingip --- heat/engine/resources/neutron/floatingip.py | 40 +++++++++++- heat/tests/test_neutron.py | 72 ++++++++++++++++++++- 2 files changed, 108 insertions(+), 4 deletions(-) diff --git a/heat/engine/resources/neutron/floatingip.py b/heat/engine/resources/neutron/floatingip.py index 389e04075..028fdf73c 100644 --- a/heat/engine/resources/neutron/floatingip.py +++ b/heat/engine/resources/neutron/floatingip.py @@ -149,16 +149,20 @@ class FloatingIPAssociation(neutron.NeutronResource): FLOATINGIP_ID: properties.Schema( properties.Schema.STRING, _('ID of the floating IP to associate.'), - required=True + required=True, + update_allowed=True ), PORT_ID: properties.Schema( properties.Schema.STRING, _('ID of an existing port with at least one IP address to ' - 'associate with this floating IP.') + 'associate with this floating IP.'), + required=True, + update_allowed=True ), FIXED_IP_ADDRESS: properties.Schema( properties.Schema.STRING, - _('IP address to use if the port has multiple addresses.') + _('IP address to use if the port has multiple addresses.'), + update_allowed=True ), } @@ -183,6 +187,36 @@ class FloatingIPAssociation(neutron.NeutronResource): except NeutronClientException as ex: self._handle_not_found_exception(ex) + def handle_update(self, json_snippet, tmpl_diff, prop_diff): + if prop_diff: + (floatingip_id, port_id) = self.resource_id.split(':') + neutron_client = self.neutron() + # if the floatingip_id is changed, disassociate the port which + # associated with the old floatingip_id + if self.FLOATINGIP_ID in prop_diff: + try: + neutron_client.update_floatingip( + floatingip_id, + {'floatingip': {'port_id': None}}) + except NeutronClientException as ex: + self._handle_not_found_exception(ex) + + # associate the floatingip with the new port + floatingip_id = (prop_diff.get(self.FLOATINGIP_ID) or + floatingip_id) + port_id = prop_diff.get(self.PORT_ID) or port_id + + fixed_ip_address = (prop_diff.get(self.FIXED_IP_ADDRESS) or + self.properties.get(self.FIXED_IP_ADDRESS)) + + request_body = { + 'floatingip': { + 'port_id': port_id, + 'fixed_ip_address': fixed_ip_address}} + + neutron_client.update_floatingip(floatingip_id, request_body) + self.resource_id_set('%s:%s' % (floatingip_id, port_id)) + def resource_mapping(): if clients.neutronclient is None: diff --git a/heat/tests/test_neutron.py b/heat/tests/test_neutron.py index 0b039d0e3..6828f7e3f 100644 --- a/heat/tests/test_neutron.py +++ b/heat/tests/test_neutron.py @@ -2010,6 +2010,7 @@ class NeutronFloatingIPTest(HeatTestCase): "status": "ACTIVE", "id": "fc68ea2c-b60b-4b4f-bd82-94ec81110766" }}) + # create as neutronclient.Client.update_floatingip( 'fc68ea2c-b60b-4b4f-bd82-94ec81110766', { @@ -2019,7 +2020,50 @@ class NeutronFloatingIPTest(HeatTestCase): "status": "ACTIVE", "id": "fc68ea2c-b60b-4b4f-bd82-94ec81110766" }}) - + # update as with port_id + neutronclient.Client.update_floatingip( + 'fc68ea2c-b60b-4b4f-bd82-94ec81110766', + { + 'floatingip': { + 'port_id': u'2146dfbf-ba77-4083-8e86-d052f671ece5', + 'fixed_ip_address': None}} + ).AndReturn({'floatingip': { + "status": "ACTIVE", + "id": "fc68ea2c-b60b-4b4f-bd82-94ec81110766" + }}) + # update as with floatingip_id + neutronclient.Client.update_floatingip( + 'fc68ea2c-b60b-4b4f-bd82-94ec81110766', + {'floatingip': { + 'port_id': None + }}).AndReturn(None) + neutronclient.Client.update_floatingip( + '2146dfbf-ba77-4083-8e86-d052f671ece5', + { + 'floatingip': { + 'port_id': u'2146dfbf-ba77-4083-8e86-d052f671ece5', + 'fixed_ip_address': None}} + ).AndReturn({'floatingip': { + "status": "ACTIVE", + "id": "2146dfbf-ba77-4083-8e86-d052f671ece5" + }}) + # update as with both + neutronclient.Client.update_floatingip( + '2146dfbf-ba77-4083-8e86-d052f671ece5', + {'floatingip': { + 'port_id': None + }}).AndReturn(None) + neutronclient.Client.update_floatingip( + 'fc68ea2c-b60b-4b4f-bd82-94ec81110766', + { + 'floatingip': { + 'port_id': u'ade6fcac-7d47-416e-a3d7-ad12efe445c1', + 'fixed_ip_address': None}} + ).AndReturn({'floatingip': { + "status": "ACTIVE", + "id": "fc68ea2c-b60b-4b4f-bd82-94ec81110766" + }}) + # delete as neutronclient.Client.update_floatingip( 'fc68ea2c-b60b-4b4f-bd82-94ec81110766', {'floatingip': { @@ -2076,6 +2120,32 @@ class NeutronFloatingIPTest(HeatTestCase): port_id = p.FnGetRefId() self.assertEqual('%s:%s' % (fip_id, port_id), fipa_id) + # test update FloatingIpAssociation with port_id + update_snippet = copy.deepcopy(fipa.parsed_template()) + update_port_id = '2146dfbf-ba77-4083-8e86-d052f671ece5' + update_snippet['Properties']['port_id'] = update_port_id + + scheduler.TaskRunner(fipa.update, update_snippet)() + self.assertEqual((fipa.UPDATE, fipa.COMPLETE), fipa.state) + + # test update FloatingIpAssociation with floatingip_id + update_snippet = copy.deepcopy(fipa.parsed_template()) + update_flip_id = '2146dfbf-ba77-4083-8e86-d052f671ece5' + update_snippet['Properties']['floatingip_id'] = update_flip_id + + scheduler.TaskRunner(fipa.update, update_snippet)() + self.assertEqual((fipa.UPDATE, fipa.COMPLETE), fipa.state) + + # test update FloatingIpAssociation with port_id and floatingip_id + update_snippet = copy.deepcopy(fipa.parsed_template()) + update_flip_id = 'fc68ea2c-b60b-4b4f-bd82-94ec81110766' + update_port_id = 'ade6fcac-7d47-416e-a3d7-ad12efe445c1' + update_snippet['Properties']['floatingip_id'] = update_flip_id + update_snippet['Properties']['port_id'] = update_port_id + + scheduler.TaskRunner(fipa.update, update_snippet)() + self.assertEqual((fipa.UPDATE, fipa.COMPLETE), fipa.state) + scheduler.TaskRunner(fipa.delete)() scheduler.TaskRunner(p.delete)() scheduler.TaskRunner(fip.delete)()