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:
huangtianhua 2014-07-24 15:06:43 +08:00
parent 945f3fe95a
commit ac45f3b6e9
2 changed files with 173 additions and 3 deletions

View File

@ -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()

View 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()