Merge "Add port_extra_properties to Nova::Server nics"

This commit is contained in:
Jenkins 2015-10-14 01:20:01 +00:00 committed by Gerrit Code Review
commit cead4b8cdf
4 changed files with 192 additions and 76 deletions

View File

@ -32,16 +32,19 @@ LOG = logging.getLogger(__name__)
class Port(neutron.NeutronResource):
PROPERTIES = (
NETWORK_ID, NETWORK, NAME, VALUE_SPECS,
ADMIN_STATE_UP, FIXED_IPS, MAC_ADDRESS,
DEVICE_ID, SECURITY_GROUPS, ALLOWED_ADDRESS_PAIRS,
DEVICE_OWNER, REPLACEMENT_POLICY, VNIC_TYPE,
PORT_SECURITY_ENABLED,
NAME, NETWORK_ID, NETWORK, FIXED_IPS, SECURITY_GROUPS,
REPLACEMENT_POLICY, DEVICE_ID, DEVICE_OWNER
) = (
'network_id', 'network', 'name', 'value_specs',
'admin_state_up', 'fixed_ips', 'mac_address',
'device_id', 'security_groups', 'allowed_address_pairs',
'device_owner', 'replacement_policy', 'binding:vnic_type',
'name', 'network_id', 'network', 'fixed_ips', 'security_groups',
'replacement_policy', 'device_id', 'device_owner'
)
EXTRA_PROPERTIES = (
VALUE_SPECS, ADMIN_STATE_UP, MAC_ADDRESS,
ALLOWED_ADDRESS_PAIRS, VNIC_TYPE, PORT_SECURITY_ENABLED,
) = (
'value_specs', 'admin_state_up', 'mac_address',
'allowed_address_pairs', 'binding:vnic_type',
'port_security_enabled',
)
@ -70,6 +73,11 @@ class Port(neutron.NeutronResource):
)
properties_schema = {
NAME: properties.Schema(
properties.Schema.STRING,
_('A symbolic name for this port.'),
update_allowed=True
),
NETWORK_ID: properties.Schema(
properties.Schema.STRING,
support_status=support.SupportStatus(
@ -97,22 +105,16 @@ class Port(neutron.NeutronResource):
constraints.CustomConstraint('neutron.network')
],
),
NAME: properties.Schema(
DEVICE_ID: properties.Schema(
properties.Schema.STRING,
_('A symbolic name for this port.'),
_('Device ID of this port.'),
update_allowed=True
),
VALUE_SPECS: properties.Schema(
properties.Schema.MAP,
_('Extra parameters to include in the "port" object in the '
'creation request.'),
default={}
),
ADMIN_STATE_UP: properties.Schema(
properties.Schema.BOOLEAN,
_('The administrative state of this port.'),
default=True,
DEVICE_OWNER: properties.Schema(
properties.Schema.STRING,
_('Name of the network owning the port. '
'The value is typically network:floatingip '
'or network:router_interface or network:dhcp'),
update_allowed=True
),
FIXED_IPS: properties.Schema(
@ -157,6 +159,40 @@ class Port(neutron.NeutronResource):
),
update_allowed=True
),
SECURITY_GROUPS: properties.Schema(
properties.Schema.LIST,
_('Security group IDs to associate with this port.'),
update_allowed=True
),
REPLACEMENT_POLICY: properties.Schema(
properties.Schema.STRING,
_('Policy on how to respond to a stack-update for this resource. '
'REPLACE_ALWAYS will replace the port regardless of any '
'property changes. AUTO will update the existing port for any '
'changed update-allowed property.'),
default='AUTO',
constraints=[
constraints.AllowedValues(['REPLACE_ALWAYS', 'AUTO']),
],
update_allowed=True
),
}
# NOTE(prazumovsky): properties_schema has been separated because some
# properties used in server for creating internal port.
extra_properties_schema = {
VALUE_SPECS: properties.Schema(
properties.Schema.MAP,
_('Extra parameters to include in the "port" object in the '
'creation request.'),
default={}
),
ADMIN_STATE_UP: properties.Schema(
properties.Schema.BOOLEAN,
_('The administrative state of this port.'),
default=True,
update_allowed=True
),
MAC_ADDRESS: properties.Schema(
properties.Schema.STRING,
_('MAC address to give to this port.'),
@ -164,16 +200,6 @@ class Port(neutron.NeutronResource):
constraints.CustomConstraint('mac_addr')
]
),
DEVICE_ID: properties.Schema(
properties.Schema.STRING,
_('Device ID of this port.'),
update_allowed=True
),
SECURITY_GROUPS: properties.Schema(
properties.Schema.LIST,
_('Security group IDs to associate with this port.'),
update_allowed=True
),
ALLOWED_ADDRESS_PAIRS: properties.Schema(
properties.Schema.LIST,
_('Additional MAC/IP address pairs allowed to pass through the '
@ -199,25 +225,6 @@ class Port(neutron.NeutronResource):
},
)
),
DEVICE_OWNER: properties.Schema(
properties.Schema.STRING,
_('Name of the network owning the port. '
'The value is typically network:floatingip '
'or network:router_interface or network:dhcp'),
update_allowed=True
),
REPLACEMENT_POLICY: properties.Schema(
properties.Schema.STRING,
_('Policy on how to respond to a stack-update for this resource. '
'REPLACE_ALWAYS will replace the port regardless of any '
'property changes. AUTO will update the existing port for any '
'changed update-allowed property.'),
default='AUTO',
constraints=[
constraints.AllowedValues(['REPLACE_ALWAYS', 'AUTO']),
],
update_allowed=True
),
VNIC_TYPE: properties.Schema(
properties.Schema.STRING,
_('The vnic type to be bound on the neutron port. '
@ -300,6 +307,11 @@ class Port(neutron.NeutronResource):
),
}
def __init__(self, name, definition, stack):
"""Overloaded init in case of merging two schemas to one."""
self.properties_schema.update(self.extra_properties_schema)
super(Port, self).__init__(name, definition, stack)
def translation_rules(self):
return [
properties.TranslationRule(

View File

@ -27,6 +27,7 @@ from heat.engine.clients import progress
from heat.engine import constraints
from heat.engine import function
from heat.engine import properties
from heat.engine.resources.openstack.neutron import port as neutron_port
from heat.engine.resources.openstack.neutron import subnet
from heat.engine.resources.openstack.nova import server_network_mixin
from heat.engine.resources import scheduler_hints as sh
@ -96,10 +97,10 @@ class Server(stack_user.StackUser, sh.SchedulerHintsMixin,
_NETWORK_KEYS = (
NETWORK_UUID, NETWORK_ID, NETWORK_FIXED_IP, NETWORK_PORT,
NETWORK_SUBNET
NETWORK_SUBNET, NETWORK_PORT_EXTRA
) = (
'uuid', 'network', 'fixed_ip', 'port',
'subnet'
'subnet', 'port_extra_properties'
)
_SOFTWARE_CONFIG_FORMATS = (
@ -121,6 +122,7 @@ class Server(stack_user.StackUser, sh.SchedulerHintsMixin,
'name', 'addresses', 'networks', 'first_address',
'instance_name', 'accessIPv4', 'accessIPv6', 'console_urls',
)
properties_schema = {
NAME: properties.Schema(
properties.Schema.STRING,
@ -364,6 +366,14 @@ class Server(stack_user.StackUser, sh.SchedulerHintsMixin,
constraints.CustomConstraint('neutron.port')
]
),
NETWORK_PORT_EXTRA: properties.Schema(
properties.Schema.MAP,
_('Dict, which has expand properties for port. '
'Used only if port property is not specified '
'for creating port.'),
schema=neutron_port.Port.extra_properties_schema,
support_status=support.SupportStatus(version='6.0.0')
),
NETWORK_SUBNET: properties.Schema(
properties.Schema.STRING,
_('Subnet in which to allocate the IP address for '

View File

@ -22,6 +22,8 @@ from heat.common.i18n import _
from heat.common.i18n import _LI
from heat.engine import resource
from heat.engine.resources.openstack.neutron import port as neutron_port
LOG = logging.getLogger(__name__)
@ -86,8 +88,19 @@ class ServerNetworkMixin(object):
name = _('%(server)s-port-%(number)s') % {'server': self.name,
'number': net_number}
kwargs = {'network_id': self._get_network_id(net_data),
'name': name}
kwargs = self._prepare_internal_port_kwargs(net_data)
kwargs['name'] = name
port = self.client('neutron').create_port({'port': kwargs})['port']
# Store ids (used for floating_ip association, updating, etc.)
# in resource's data.
self._data_update_ports(port['id'], 'add')
return port['id']
def _prepare_internal_port_kwargs(self, net_data):
kwargs = {'network_id': self._get_network_id(net_data)}
fixed_ip = net_data.get(self.NETWORK_FIXED_IP)
subnet = net_data.get(self.NETWORK_SUBNET)
body = {}
@ -99,13 +112,34 @@ class ServerNetworkMixin(object):
if body:
kwargs.update({'fixed_ips': [body]})
port = self.client('neutron').create_port({'port': kwargs})['port']
if net_data.get(self.SECURITY_GROUPS):
sec_uuids = self.client_plugin(
'neutron').get_secgroup_uuids(net_data.get(
self.SECURITY_GROUPS))
kwargs['security_groups'] = sec_uuids
# Store ids (used for floating_ip association, updating, etc.)
# in resource's data.
self._data_update_ports(port['id'], 'add')
extra_props = net_data.get(self.NETWORK_PORT_EXTRA)
if extra_props is not None:
port_extra_keys = list(neutron_port.Port.EXTRA_PROPERTIES)
port_extra_keys.remove(neutron_port.Port.ALLOWED_ADDRESS_PAIRS)
for key in port_extra_keys:
if extra_props.get(key) is not None:
kwargs[key] = extra_props.get(key)
return port['id']
allowed_address_pairs = extra_props.get(
neutron_port.Port.ALLOWED_ADDRESS_PAIRS)
if allowed_address_pairs is not None:
for pair in allowed_address_pairs:
if (neutron_port.Port.ALLOWED_ADDRESS_PAIR_MAC_ADDRESS
in pair and pair.get(
neutron_port.Port.ALLOWED_ADDRESS_PAIR_MAC_ADDRESS)
is None):
del pair[
neutron_port.Port.ALLOWED_ADDRESS_PAIR_MAC_ADDRESS]
kwargs[neutron_port.Port.ALLOWED_ADDRESS_PAIRS] = \
allowed_address_pairs
return kwargs
def _delete_internal_port(self, port_id):
"""Delete physical port by id."""

View File

@ -2996,10 +2996,12 @@ class ServersTest(common.HeatTestCase):
scheduler.TaskRunner(server.create)()
self.m.VerifyAll()
def create_old_net(self, port=None, net=None, ip=None, uuid=None,
subnet=None):
def create_old_net(self, port=None, net=None,
ip=None, uuid=None, subnet=None,
port_extra_properties=None):
return {'port': port, 'network': net, 'fixed_ip': ip, 'uuid': uuid,
'subnet': subnet}
'subnet': subnet,
'port_extra_properties': port_extra_properties}
def create_fake_iface(self, port, net, ip):
class fake_interface(object):
@ -3082,7 +3084,8 @@ class ServersTest(common.HeatTestCase):
new_nets_copy = copy.deepcopy(new_nets)
old_nets_copy = copy.deepcopy(old_nets)
for net in new_nets_copy:
for key in ('port', 'network', 'fixed_ip', 'uuid', 'subnet'):
for key in ('port', 'network', 'fixed_ip', 'uuid', 'subnet',
'port_extra_properties'):
net.setdefault(key)
matched_nets = server._exclude_not_updated_networks(old_nets,
@ -3112,7 +3115,8 @@ class ServersTest(common.HeatTestCase):
new_nets_copy = copy.deepcopy(new_nets)
old_nets_copy = copy.deepcopy(old_nets)
for net in new_nets_copy:
for key in ('port', 'network', 'fixed_ip', 'uuid', 'subnet'):
for key in ('port', 'network', 'fixed_ip', 'uuid', 'subnet',
'port_extra_properties'):
net.setdefault(key)
matched_nets = server._exclude_not_updated_networks(old_nets, new_nets)
@ -3135,7 +3139,8 @@ class ServersTest(common.HeatTestCase):
'fixed_ip': None,
'port': None,
'uuid': None,
'subnet': None}]
'subnet': None,
'port_extra_properties': None}]
new_nets_copy = copy.deepcopy(new_nets)
matched_nets = server._exclude_not_updated_networks(old_nets, new_nets)
@ -3176,27 +3181,33 @@ class ServersTest(common.HeatTestCase):
'network': None,
'fixed_ip': None,
'uuid': None,
'subnet': None},
'subnet': None,
'port_extra_properties': None,
'uuid': None},
{'port': 'bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb',
'network': 'gggggggg-1111-1111-1111-gggggggggggg',
'fixed_ip': '1.2.3.4',
'uuid': None,
'subnet': None},
'subnet': None,
'port_extra_properties': None,
'uuid': None},
{'port': 'cccccccc-cccc-cccc-cccc-cccccccccccc',
'network': 'gggggggg-1111-1111-1111-gggggggggggg',
'fixed_ip': None,
'uuid': None,
'subnet': None},
'subnet': None,
'port_extra_properties': None,
'uuid': None},
{'port': 'dddddddd-dddd-dddd-dddd-dddddddddddd',
'network': None,
'fixed_ip': None,
'uuid': None,
'subnet': None},
'subnet': None,
'port_extra_properties': None,
'uuid': None},
{'port': 'eeeeeeee-eeee-eeee-eeee-eeeeeeeeeeee',
'uuid': 'gggggggg-1111-1111-1111-gggggggggggg',
'fixed_ip': '5.6.7.8',
'network': None,
'subnet': None}]
'subnet': None,
'port_extra_properties': None,
'network': None}]
self.patchobject(neutron.NeutronClientPlugin, 'resolve_network',
return_value='gggggggg-1111-1111-1111-gggggggggggg')
@ -3901,7 +3912,7 @@ class ServerInternalPortTest(common.HeatTestCase):
self.assertEqual('Specified subnet 1234 does not belongs to '
'network 4321.', six.text_type(ex))
def test_build_nics_create_internal_port_all_props(self):
def test_build_nics_create_internal_port_all_props_without_extras(self):
tmpl = """
heat_template_version: 2015-10-15
resources:
@ -3952,6 +3963,55 @@ class ServerInternalPortTest(common.HeatTestCase):
self.assertFalse(self.port_create.called)
self.assertFalse(data_set.called)
def test_prepare_port_kwargs_with_extras(self):
tmpl = """
heat_template_version: 2015-10-15
resources:
server:
type: OS::Nova::Server
properties:
flavor: m1.small
image: F17-x86_64-gold
networks:
- network: 4321
subnet: 1234
fixed_ip: 127.0.0.1
port_extra_properties:
mac_address: 00:00:00:00:00:00
allowed_address_pairs:
- ip_address: 127.0.0.1
mac_address: None
- mac_address: 00:00:00:00:00:00
"""
t, stack, server = self._return_template_stack_and_rsrc_defn('test',
tmpl)
self.resolve.side_effect = ['4321', '1234']
network = {'network': '4321', 'subnet': '1234',
'fixed_ip': '127.0.0.1',
'port_extra_properties': {
'mac_address': '00:00:00:00:00:00',
'allowed_address_pairs': [
{'ip_address': '127.0.0.1',
'mac_address': None},
{'mac_address': '00:00:00:00:00:00'}
]
}}
kwargs = server._prepare_internal_port_kwargs(network)
self.assertEqual({'network_id': '4321',
'fixed_ips': [
{'ip_address': '127.0.0.1', 'subnet_id': '1234'}
],
'mac_address': '00:00:00:00:00:00',
'allowed_address_pairs': [
{'ip_address': '127.0.0.1'},
{'mac_address': '00:00:00:00:00:00'}]},
kwargs)
def test_build_nics_create_internal_port_without_net(self):
tmpl = """
heat_template_version: 2015-10-15