Implement GroupSet updatable for AWS::EC2::NetworkInterface
Implement the 'GroupSet' updatable for AWS::EC2::NetworkInterface resource to be compatible with AWSCloudFormation. Change-Id: Ia92c6d2732fdafc659d1a9a1db01cb66be9a9bc0 Closes-Bug: #1334212
This commit is contained in:
parent
945f3fe95a
commit
ac45f3b6e9
@ -46,7 +46,7 @@ class NetworkInterface(resource.Resource):
|
|||||||
GROUP_SET: properties.Schema(
|
GROUP_SET: properties.Schema(
|
||||||
properties.Schema.LIST,
|
properties.Schema.LIST,
|
||||||
_('List of security group IDs associated with this interface.'),
|
_('List of security group IDs associated with this interface.'),
|
||||||
default=[]
|
update_allowed=True
|
||||||
),
|
),
|
||||||
PRIVATE_IP_ADDRESS: properties.Schema(
|
PRIVATE_IP_ADDRESS: properties.Schema(
|
||||||
properties.Schema.STRING
|
properties.Schema.STRING
|
||||||
@ -115,8 +115,11 @@ class NetworkInterface(resource.Resource):
|
|||||||
'network_id': network_id,
|
'network_id': network_id,
|
||||||
'fixed_ips': [fixed_ip]
|
'fixed_ips': [fixed_ip]
|
||||||
}
|
}
|
||||||
|
# if without group_set, don't set the 'security_groups' property,
|
||||||
if self.properties[self.GROUP_SET]:
|
# neutron will create the port with the 'default' securityGroup,
|
||||||
|
# if has the group_set and the value is [], which means to create the
|
||||||
|
# port without securityGroup(same as the behavior of neutron)
|
||||||
|
if self.properties[self.GROUP_SET] is not None:
|
||||||
sgs = self.client_plugin().get_secgroup_uuids(
|
sgs = self.client_plugin().get_secgroup_uuids(
|
||||||
self.properties.get(self.GROUP_SET))
|
self.properties.get(self.GROUP_SET))
|
||||||
props['security_groups'] = sgs
|
props['security_groups'] = sgs
|
||||||
@ -124,12 +127,34 @@ class NetworkInterface(resource.Resource):
|
|||||||
self.resource_id_set(port['id'])
|
self.resource_id_set(port['id'])
|
||||||
|
|
||||||
def handle_delete(self):
|
def handle_delete(self):
|
||||||
|
if self.resource_id is None:
|
||||||
|
return
|
||||||
|
|
||||||
client = self.neutron()
|
client = self.neutron()
|
||||||
try:
|
try:
|
||||||
client.delete_port(self.resource_id)
|
client.delete_port(self.resource_id)
|
||||||
except Exception as ex:
|
except Exception as ex:
|
||||||
self.client_plugin().ignore_not_found(ex)
|
self.client_plugin().ignore_not_found(ex)
|
||||||
|
|
||||||
|
def handle_update(self, json_snippet, tmpl_diff, prop_diff):
|
||||||
|
if prop_diff:
|
||||||
|
update_props = {}
|
||||||
|
if self.GROUP_SET in prop_diff:
|
||||||
|
group_set = prop_diff.get(self.GROUP_SET)
|
||||||
|
# update should keep the same behavior as creation,
|
||||||
|
# if without the GroupSet in update template, we should
|
||||||
|
# update the security_groups property to referent
|
||||||
|
# the 'default' security group
|
||||||
|
if group_set is not None:
|
||||||
|
sgs = self.client_plugin().get_secgroup_uuids(group_set)
|
||||||
|
else:
|
||||||
|
sgs = self.client_plugin().get_secgroup_uuids(['default'])
|
||||||
|
|
||||||
|
update_props['security_groups'] = sgs
|
||||||
|
|
||||||
|
self.neutron().update_port(self.resource_id,
|
||||||
|
{'port': update_props})
|
||||||
|
|
||||||
def _get_fixed_ip_address(self, ):
|
def _get_fixed_ip_address(self, ):
|
||||||
if self.fixed_ip_address is None:
|
if self.fixed_ip_address is None:
|
||||||
client = self.neutron()
|
client = self.neutron()
|
||||||
|
145
heat/tests/test_network_interface.py
Normal file
145
heat/tests/test_network_interface.py
Normal file
@ -0,0 +1,145 @@
|
|||||||
|
#
|
||||||
|
# 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 heat.engine import rsrc_defn
|
||||||
|
from heat.engine import scheduler
|
||||||
|
from heat.tests.common import HeatTestCase
|
||||||
|
from heat.tests import utils
|
||||||
|
|
||||||
|
try:
|
||||||
|
from neutronclient.v2_0 import client as neutronclient
|
||||||
|
except ImportError:
|
||||||
|
neutronclient = None
|
||||||
|
|
||||||
|
test_template = {
|
||||||
|
'heat_template_version': '2013-05-23',
|
||||||
|
'resources': {
|
||||||
|
'my_nic': {
|
||||||
|
'type': 'AWS::EC2::NetworkInterface',
|
||||||
|
'properties': {
|
||||||
|
'SubnetId': 'ssss'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class NetworkInterfaceTest(HeatTestCase):
|
||||||
|
def setUp(self):
|
||||||
|
super(NetworkInterfaceTest, self).setUp()
|
||||||
|
self.ctx = utils.dummy_context()
|
||||||
|
self.m.StubOutWithMock(neutronclient.Client, 'show_subnet')
|
||||||
|
self.m.StubOutWithMock(neutronclient.Client, 'create_port')
|
||||||
|
self.m.StubOutWithMock(neutronclient.Client, 'delete_port')
|
||||||
|
self.m.StubOutWithMock(neutronclient.Client, 'update_port')
|
||||||
|
self.stub_keystoneclient()
|
||||||
|
|
||||||
|
def mock_show_subnet(self):
|
||||||
|
neutronclient.Client.show_subnet('ssss').AndReturn({
|
||||||
|
'subnet': {
|
||||||
|
'name': 'my_subnet',
|
||||||
|
'network_id': 'nnnn',
|
||||||
|
'tenant_id': 'c1210485b2424d48804aad5d39c61b8f',
|
||||||
|
'allocation_pools': [{'start': '10.0.0.2',
|
||||||
|
'end': '10.0.0.254'}],
|
||||||
|
'gateway_ip': '10.0.0.1',
|
||||||
|
'ip_version': 4,
|
||||||
|
'cidr': '10.0.0.0/24',
|
||||||
|
'id': 'ssss',
|
||||||
|
'enable_dhcp': False,
|
||||||
|
}})
|
||||||
|
|
||||||
|
def mock_create_network_interface(self, stack_name='my_stack',
|
||||||
|
resource_name='my_nic',
|
||||||
|
security_groups=None):
|
||||||
|
self.nic_name = utils.PhysName(stack_name, resource_name)
|
||||||
|
port = {'network_id': 'nnnn',
|
||||||
|
'fixed_ips': [{
|
||||||
|
'subnet_id': u'ssss'
|
||||||
|
}],
|
||||||
|
'name': self.nic_name,
|
||||||
|
'admin_state_up': True}
|
||||||
|
|
||||||
|
port_info = {
|
||||||
|
'port': {
|
||||||
|
'admin_state_up': True,
|
||||||
|
'device_id': '',
|
||||||
|
'device_owner': '',
|
||||||
|
'fixed_ips': [
|
||||||
|
{
|
||||||
|
'ip_address': '10.0.0.100',
|
||||||
|
'subnet_id': 'ssss'
|
||||||
|
}
|
||||||
|
],
|
||||||
|
'id': 'pppp',
|
||||||
|
'mac_address': 'fa:16:3e:25:32:5d',
|
||||||
|
'name': self.nic_name,
|
||||||
|
'network_id': 'nnnn',
|
||||||
|
'status': 'ACTIVE',
|
||||||
|
'tenant_id': 'c1210485b2424d48804aad5d39c61b8f'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if security_groups is not None:
|
||||||
|
port['security_groups'] = security_groups
|
||||||
|
port_info['security_groups'] = security_groups
|
||||||
|
else:
|
||||||
|
port_info['security_groups'] = ['default']
|
||||||
|
|
||||||
|
neutronclient.Client.create_port({'port': port}).AndReturn(port_info)
|
||||||
|
|
||||||
|
def mock_update_network_interface(self, update_props, port_id='pppp'):
|
||||||
|
neutronclient.Client.update_port(
|
||||||
|
port_id,
|
||||||
|
{'port': update_props}).AndReturn(None)
|
||||||
|
|
||||||
|
def mock_delete_network_interface(self, port_id='pppp'):
|
||||||
|
neutronclient.Client.delete_port(port_id).AndReturn(None)
|
||||||
|
|
||||||
|
def test_network_interface_create_update_delete(self):
|
||||||
|
my_stack = utils.parse_stack(test_template, stack_name='my_stack')
|
||||||
|
nic_rsrc = my_stack['my_nic']
|
||||||
|
|
||||||
|
self.mock_show_subnet()
|
||||||
|
self.mock_create_network_interface()
|
||||||
|
|
||||||
|
update_props = {}
|
||||||
|
update_sg_ids = ['0389f747-7785-4757-b7bb-2ab07e4b09c3']
|
||||||
|
update_props['security_groups'] = update_sg_ids
|
||||||
|
|
||||||
|
self.mock_update_network_interface(update_props)
|
||||||
|
self.mock_delete_network_interface()
|
||||||
|
|
||||||
|
self.m.ReplayAll()
|
||||||
|
# create the nic without GroupSet
|
||||||
|
self.assertIsNone(nic_rsrc.validate())
|
||||||
|
scheduler.TaskRunner(nic_rsrc.create)()
|
||||||
|
self.assertEqual((nic_rsrc.CREATE, my_stack.COMPLETE),
|
||||||
|
nic_rsrc.state)
|
||||||
|
|
||||||
|
# update the nic with GroupSet
|
||||||
|
props = copy.deepcopy(nic_rsrc.properties.data)
|
||||||
|
props['GroupSet'] = update_sg_ids
|
||||||
|
update_snippet = rsrc_defn.ResourceDefinition(nic_rsrc.name,
|
||||||
|
nic_rsrc.type(),
|
||||||
|
props)
|
||||||
|
scheduler.TaskRunner(nic_rsrc.update, update_snippet)()
|
||||||
|
self.assertEqual((nic_rsrc.UPDATE, nic_rsrc.COMPLETE), nic_rsrc.state)
|
||||||
|
|
||||||
|
# delete the nic
|
||||||
|
scheduler.TaskRunner(nic_rsrc.delete)()
|
||||||
|
self.assertEqual((nic_rsrc.DELETE, nic_rsrc.COMPLETE), nic_rsrc.state)
|
||||||
|
|
||||||
|
self.m.VerifyAll()
|
Loading…
Reference in New Issue
Block a user