Implement AWS::EC2::EIPAssociation updatable
Change-Id: Iedb9a1a42e134e9c524311747d6253a0199ac1d4
This commit is contained in:
parent
35d7465c11
commit
0c2ee06320
@ -184,19 +184,23 @@ class ElasticIpAssociation(resource.Resource):
|
||||
properties_schema = {
|
||||
INSTANCE_ID: properties.Schema(
|
||||
properties.Schema.STRING,
|
||||
_('Instance ID to associate with EIP specified by EIP property.')
|
||||
_('Instance ID to associate with EIP specified by EIP property.'),
|
||||
update_allowed=True
|
||||
),
|
||||
EIP: properties.Schema(
|
||||
properties.Schema.STRING,
|
||||
_('EIP address to associate with instance.')
|
||||
_('EIP address to associate with instance.'),
|
||||
update_allowed=True
|
||||
),
|
||||
ALLOCATION_ID: properties.Schema(
|
||||
properties.Schema.STRING,
|
||||
_('Allocation ID for VPC EIP address.')
|
||||
_('Allocation ID for VPC EIP address.'),
|
||||
update_allowed=True
|
||||
),
|
||||
NETWORK_INTERFACE_ID: properties.Schema(
|
||||
properties.Schema.STRING,
|
||||
_('Network interface ID to associate with EIP.')
|
||||
_('Network interface ID to associate with EIP.'),
|
||||
update_allowed=True
|
||||
),
|
||||
}
|
||||
|
||||
@ -276,6 +280,93 @@ class ElasticIpAssociation(resource.Resource):
|
||||
|
||||
return server
|
||||
|
||||
def _floatingIp_detach(self,
|
||||
nova_ignore_not_found=False,
|
||||
neutron_ignore_not_found=False):
|
||||
eip = self.properties[self.EIP]
|
||||
allocation_id = self.properties[self.ALLOCATION_ID]
|
||||
instance_id = self.properties[self.INSTANCE_ID]
|
||||
server = None
|
||||
if eip:
|
||||
# if has eip_old, to remove the eip_old from the instance
|
||||
server = self._nova_remove_floating_ip(instance_id,
|
||||
eip,
|
||||
nova_ignore_not_found)
|
||||
else:
|
||||
# if hasn't eip_old, to update neutron floatingIp
|
||||
self._neutron_update_floating_ip(allocation_id,
|
||||
None,
|
||||
neutron_ignore_not_found)
|
||||
|
||||
return server
|
||||
|
||||
def _handle_update_eipInfo(self, prop_diff):
|
||||
eip_update = prop_diff.get(self.EIP)
|
||||
allocation_id_update = prop_diff.get(self.ALLOCATION_ID)
|
||||
instance_id = self.properties[self.INSTANCE_ID]
|
||||
ni_id = self.properties[self.NETWORK_INTERFACE_ID]
|
||||
if eip_update:
|
||||
server = self._floatingIp_detach(neutron_ignore_not_found=True)
|
||||
if server:
|
||||
# then to attach the eip_update to the instance
|
||||
server.add_floating_ip(eip_update)
|
||||
self.resource_id_set(eip_update)
|
||||
elif allocation_id_update:
|
||||
self._floatingIp_detach(nova_ignore_not_found=True)
|
||||
port_id, port_rsrc = self._get_port_info(ni_id, instance_id)
|
||||
if not port_id or not port_rsrc:
|
||||
LOG.error(_('Port not specified.'))
|
||||
raise exception.NotFound(_('Failed to update, can not found '
|
||||
'port info.'))
|
||||
|
||||
network_id = port_rsrc['network_id']
|
||||
self._neutron_add_gateway_router(allocation_id_update, network_id)
|
||||
self._neutron_update_floating_ip(allocation_id_update, port_id)
|
||||
self.resource_id_set(allocation_id_update)
|
||||
|
||||
def _handle_update_portInfo(self, prop_diff):
|
||||
instance_id_update = prop_diff.get(self.INSTANCE_ID)
|
||||
ni_id_update = prop_diff.get(self.NETWORK_INTERFACE_ID)
|
||||
eip = self.properties[self.EIP]
|
||||
allocation_id = self.properties[self.ALLOCATION_ID]
|
||||
# if update portInfo, no need to detach the port from
|
||||
# old instance/floatingip.
|
||||
if eip:
|
||||
server = self.nova().servers.get(instance_id_update)
|
||||
server.add_floating_ip(eip)
|
||||
else:
|
||||
port_id, port_rsrc = self._get_port_info(ni_id_update,
|
||||
instance_id_update)
|
||||
if not port_id or not port_rsrc:
|
||||
LOG.error(_('Port not specified.'))
|
||||
raise exception.NotFound(_('Failed to update, can not found '
|
||||
'port info.'))
|
||||
|
||||
network_id = port_rsrc['network_id']
|
||||
self._neutron_add_gateway_router(allocation_id, network_id)
|
||||
self._neutron_update_floating_ip(allocation_id, port_id)
|
||||
|
||||
def _validate_update_properties(self, prop_diff):
|
||||
# according to aws doc, when update allocation_id or eip,
|
||||
# if you also change the InstanceId or NetworkInterfaceId,
|
||||
# should go to Replacement flow
|
||||
if self.ALLOCATION_ID in prop_diff or self.EIP in prop_diff:
|
||||
instance_id = prop_diff.get(self.INSTANCE_ID)
|
||||
ni_id = prop_diff.get(self.NETWORK_INTERFACE_ID)
|
||||
|
||||
if instance_id or ni_id:
|
||||
raise resource.UpdateReplace(self.name)
|
||||
|
||||
# according to aws doc, when update the instance_id or
|
||||
# network_interface_id, if you also change the EIP or
|
||||
# ALLOCATION_ID, should go to Replacement flow
|
||||
if (self.INSTANCE_ID in prop_diff or
|
||||
self.NETWORK_INTERFACE_ID in prop_diff):
|
||||
eip = prop_diff.get(self.EIP)
|
||||
allocation_id = prop_diff.get(self.ALLOCATION_ID)
|
||||
if eip or allocation_id:
|
||||
raise resource.UpdateReplace(self.name)
|
||||
|
||||
def handle_create(self):
|
||||
"""Add a floating IP address to a server."""
|
||||
if self.properties[self.EIP]:
|
||||
@ -319,6 +410,15 @@ class ElasticIpAssociation(resource.Resource):
|
||||
port_id=None,
|
||||
ignore_not_found=True)
|
||||
|
||||
def handle_update(self, json_snippet, tmpl_diff, prop_diff):
|
||||
if prop_diff:
|
||||
self._validate_update_properties(prop_diff)
|
||||
if self.ALLOCATION_ID in prop_diff or self.EIP in prop_diff:
|
||||
self._handle_update_eipInfo(prop_diff)
|
||||
elif (self.INSTANCE_ID in prop_diff or
|
||||
self.NETWORK_INTERFACE_ID in prop_diff):
|
||||
self._handle_update_portInfo(prop_diff)
|
||||
|
||||
|
||||
def resource_mapping():
|
||||
return {
|
||||
|
@ -482,8 +482,8 @@ class AllocTest(HeatTestCase):
|
||||
id = 'fc68ea2c-b60b-4b4f-bd82-94ec81110766'
|
||||
neutronclient.Client.delete_floatingip(id).AndReturn(None)
|
||||
|
||||
def mock_list_ports(self):
|
||||
neutronclient.Client.list_ports(id='the_nic').AndReturn(
|
||||
def mock_list_ports(self, id='the_nic'):
|
||||
neutronclient.Client.list_ports(id=id).AndReturn(
|
||||
{"ports": [{
|
||||
"status": "DOWN",
|
||||
"binding:host_id": "null",
|
||||
@ -689,3 +689,152 @@ class AllocTest(HeatTestCase):
|
||||
self.assertEqual((rsrc.DELETE, rsrc.COMPLETE), rsrc.state)
|
||||
|
||||
self.m.VerifyAll()
|
||||
|
||||
def test_update_association_with_InstanceId(self):
|
||||
nova.NovaClientPlugin._create().AndReturn(self.fc)
|
||||
server = self.fc.servers.list()[0]
|
||||
self.fc.servers.get('WebServer').MultipleTimes() \
|
||||
.AndReturn(server)
|
||||
server_update = self.fc.servers.list()[1]
|
||||
self.fc.servers.get('5678').AndReturn(server_update)
|
||||
|
||||
self.m.ReplayAll()
|
||||
|
||||
t = template_format.parse(eip_template_ipassoc)
|
||||
stack = utils.parse_stack(t)
|
||||
self.create_eip(t, stack, 'IPAddress')
|
||||
ass = self.create_association(t, stack, 'IPAssoc')
|
||||
self.assertEqual('11.0.0.1', ass.properties['EIP'])
|
||||
|
||||
# update with the new InstanceId
|
||||
props = copy.deepcopy(ass.properties.data)
|
||||
update_server_id = '5678'
|
||||
props['InstanceId'] = update_server_id
|
||||
update_snippet = rsrc_defn.ResourceDefinition(ass.name, ass.type(),
|
||||
stack.t.parse(stack,
|
||||
props))
|
||||
scheduler.TaskRunner(ass.update, update_snippet)()
|
||||
self.assertEqual((ass.UPDATE, ass.COMPLETE), ass.state)
|
||||
|
||||
self.m.VerifyAll()
|
||||
|
||||
def test_update_association_with_EIP(self):
|
||||
nova.NovaClientPlugin._create().AndReturn(self.fc)
|
||||
server = self.fc.servers.list()[0]
|
||||
self.fc.servers.get('WebServer').MultipleTimes() \
|
||||
.AndReturn(server)
|
||||
|
||||
self.m.ReplayAll()
|
||||
|
||||
t = template_format.parse(eip_template_ipassoc)
|
||||
stack = utils.parse_stack(t)
|
||||
self.create_eip(t, stack, 'IPAddress')
|
||||
ass = self.create_association(t, stack, 'IPAssoc')
|
||||
|
||||
# update with the new EIP
|
||||
props = copy.deepcopy(ass.properties.data)
|
||||
update_eip = '11.0.0.2'
|
||||
props['EIP'] = update_eip
|
||||
update_snippet = rsrc_defn.ResourceDefinition(ass.name, ass.type(),
|
||||
stack.t.parse(stack,
|
||||
props))
|
||||
scheduler.TaskRunner(ass.update, update_snippet)()
|
||||
self.assertEqual((ass.UPDATE, ass.COMPLETE), ass.state)
|
||||
|
||||
self.m.VerifyAll()
|
||||
|
||||
def test_update_association_with_AllocationId_or_EIP(self):
|
||||
nova.NovaClientPlugin._create().AndReturn(self.fc)
|
||||
server = self.fc.servers.list()[0]
|
||||
self.fc.servers.get('WebServer').MultipleTimes()\
|
||||
.AndReturn(server)
|
||||
|
||||
self.mock_list_instance_ports('WebServer')
|
||||
self.mock_show_network()
|
||||
self.mock_no_router_for_vpc()
|
||||
self.mock_update_floatingip(
|
||||
port='a000228d-b40b-4124-8394-a4082ae1b76c')
|
||||
|
||||
self.mock_update_floatingip(port=None)
|
||||
self.m.ReplayAll()
|
||||
|
||||
t = template_format.parse(eip_template_ipassoc)
|
||||
stack = utils.parse_stack(t)
|
||||
self.create_eip(t, stack, 'IPAddress')
|
||||
ass = self.create_association(t, stack, 'IPAssoc')
|
||||
self.assertEqual('11.0.0.1', ass.properties['EIP'])
|
||||
|
||||
# change EIP to AllocationId
|
||||
props = copy.deepcopy(ass.properties.data)
|
||||
update_allocationId = 'fc68ea2c-b60b-4b4f-bd82-94ec81110766'
|
||||
props['AllocationId'] = update_allocationId
|
||||
props.pop('EIP')
|
||||
update_snippet = rsrc_defn.ResourceDefinition(ass.name, ass.type(),
|
||||
stack.t.parse(stack,
|
||||
props))
|
||||
scheduler.TaskRunner(ass.update, update_snippet)()
|
||||
self.assertEqual((ass.UPDATE, ass.COMPLETE), ass.state)
|
||||
|
||||
# change AllocationId to EIP
|
||||
props = copy.deepcopy(ass.properties.data)
|
||||
update_eip = '11.0.0.2'
|
||||
props['EIP'] = update_eip
|
||||
props.pop('AllocationId')
|
||||
update_snippet = rsrc_defn.ResourceDefinition(ass.name, ass.type(),
|
||||
stack.t.parse(stack,
|
||||
props))
|
||||
scheduler.TaskRunner(ass.update, update_snippet)()
|
||||
self.assertEqual((ass.UPDATE, ass.COMPLETE), ass.state)
|
||||
|
||||
self.m.VerifyAll()
|
||||
|
||||
def test_update_association_with_NetworkInterfaceId_or_InstanceId(self):
|
||||
self.mock_create_floatingip()
|
||||
self.mock_list_ports()
|
||||
self.mock_show_network()
|
||||
self.mock_no_router_for_vpc()
|
||||
self.mock_update_floatingip()
|
||||
|
||||
self.mock_list_ports(id='a000228d-b40b-4124-8394-a4082ae1b76b')
|
||||
self.mock_show_network()
|
||||
self.mock_no_router_for_vpc()
|
||||
self.mock_update_floatingip(
|
||||
port='a000228d-b40b-4124-8394-a4082ae1b76b')
|
||||
|
||||
self.mock_list_instance_ports('5678')
|
||||
self.mock_show_network()
|
||||
self.mock_no_router_for_vpc()
|
||||
self.mock_update_floatingip(
|
||||
port='a000228d-b40b-4124-8394-a4082ae1b76c')
|
||||
|
||||
self.m.ReplayAll()
|
||||
|
||||
t = template_format.parse(eip_template_ipassoc2)
|
||||
stack = utils.parse_stack(t)
|
||||
self.create_eip(t, stack, 'the_eip')
|
||||
ass = self.create_association(t, stack, 'IPAssoc')
|
||||
|
||||
# update with the new NetworkInterfaceId
|
||||
props = copy.deepcopy(ass.properties.data)
|
||||
update_networkInterfaceId = 'a000228d-b40b-4124-8394-a4082ae1b76b'
|
||||
props['NetworkInterfaceId'] = update_networkInterfaceId
|
||||
|
||||
update_snippet = rsrc_defn.ResourceDefinition(ass.name, ass.type(),
|
||||
stack.t.parse(stack,
|
||||
props))
|
||||
scheduler.TaskRunner(ass.update, update_snippet)()
|
||||
self.assertEqual((ass.UPDATE, ass.COMPLETE), ass.state)
|
||||
|
||||
# update with the InstanceId
|
||||
props = copy.deepcopy(ass.properties.data)
|
||||
instance_id = '5678'
|
||||
props.pop('NetworkInterfaceId')
|
||||
props['InstanceId'] = instance_id
|
||||
|
||||
update_snippet = rsrc_defn.ResourceDefinition(ass.name, ass.type(),
|
||||
stack.t.parse(stack,
|
||||
props))
|
||||
scheduler.TaskRunner(ass.update, update_snippet)()
|
||||
self.assertEqual((ass.UPDATE, ass.COMPLETE), ass.state)
|
||||
|
||||
self.m.VerifyAll()
|
||||
|
Loading…
Reference in New Issue
Block a user