Skip to content

Commit

Permalink
Pass instance host-id to Quantum using port bindings extension.
Browse files Browse the repository at this point in the history
This is a nova change to pass the hostname on which the instance was started to
Quantum. The associated quantum plugins will pass this hostname to
hardware device vendors management stations to allow them to segment the network
in a more precise manner (for example automatically trunk the vlan on the
physical switch port connected to the compute host on which the VM instance was
started).

Implements: blueprint vm-host-quantum

Change-Id: I05012ac7abf67a94b515361a7ef7ac1063cb0ac3
  • Loading branch information
gongysh authored and gongysh committed Jul 2, 2013
1 parent 88531ad commit 7de916f
Show file tree
Hide file tree
Showing 3 changed files with 280 additions and 133 deletions.
73 changes: 50 additions & 23 deletions nova/network/quantumv2/api.py
Expand Up @@ -28,6 +28,7 @@
from nova.network import api as network_api
from nova.network import model as network_model
from nova.network import quantumv2
from nova.network.quantumv2 import constants
from nova.network.security_group import openstack_driver
from nova.openstack.common import excutils
from nova.openstack.common import log as logging
Expand Down Expand Up @@ -76,8 +77,6 @@
CONF.import_opt('flat_injected', 'nova.network.manager')
LOG = logging.getLogger(__name__)

NET_EXTERNAL = 'router:external'

refresh_cache = network_api.refresh_cache
update_instance_info_cache = network_api.update_instance_cache_with_nw_info

Expand Down Expand Up @@ -242,8 +241,14 @@ def allocate_for_instance(self, context, instance, **kwargs):
'device_owner': zone}}
try:
port = ports.get(network_id)
self._populate_quantum_extension_values(instance,
port_req_body)
# Requires admin creds to set port bindings
port_client = (quantum if not
self._has_port_binding_extension() else
quantumv2.get_client(context, admin=True))
if port:
quantum.update_port(port['id'], port_req_body)
port_client.update_port(port['id'], port_req_body)
touched_port_ids.append(port['id'])
else:
fixed_ip = fixed_ips.get(network_id)
Expand All @@ -262,28 +267,31 @@ def allocate_for_instance(self, context, instance, **kwargs):
instance=instance['display_name'])
mac_address = available_macs.pop()
port_req_body['port']['mac_address'] = mac_address

self._populate_quantum_extension_values(instance,
port_req_body)
created_port_ids.append(
quantum.create_port(port_req_body)['port']['id'])
port_client.create_port(port_req_body)['port']['id'])
except Exception:
with excutils.save_and_reraise_exception():
for port_id in touched_port_ids:
port_in_server = quantum.show_port(port_id).get('port')
if not port_in_server:
raise Exception(_('Port not found'))
port_req_body = {'port': {'device_id': None}}
quantum.update_port(port_id, port_req_body)
try:
port_req_body = {'port': {'device_id': None}}
# Requires admin creds to set port bindings
if self._has_port_binding_extension():
port_req_body['port']['binding:host_id'] = None
port_client = quantumv2.get_client(
context, admin=True)
else:
port_client = quantum
port_client.update_port(port_id, port_req_body)
except Exception:
msg = _("Failed to update port %s")
LOG.exception(msg, port_id)

for port_id in created_port_ids:
try:
quantum.delete_port(port_id)
except Exception as ex:
msg = _("Fail to delete port %(portid)s with"
" failure: %(exception)s")
LOG.debug(msg, {'portid': port_id,
'exception': ex})
except Exception:
msg = _("Failed to delete port %s")
LOG.exception(msg, port_id)

nw_info = self._get_instance_nw_info(context, instance, networks=nets)
# NOTE(danms): Only return info about ports we created in this run.
Expand All @@ -307,6 +315,11 @@ def _refresh_quantum_extensions_cache(self):
self.extensions = dict((ext['name'], ext)
for ext in extensions_list)

def _has_port_binding_extension(self, refresh_cache=False):
if refresh_cache:
self._refresh_quantum_extensions_cache()
return constants.PORTBINDING_EXT in self.extensions

def _populate_quantum_extension_values(self, instance, port_req_body):
"""Populate quantum extension values for the instance.
Expand All @@ -317,6 +330,8 @@ def _populate_quantum_extension_values(self, instance, port_req_body):
instance_type = flavors.extract_flavor(instance)
rxtx_factor = instance_type.get('rxtx_factor')
port_req_body['port']['rxtx_factor'] = rxtx_factor
if self._has_port_binding_extension():
port_req_body['port']['binding:host_id'] = instance.get('host')

def deallocate_for_instance(self, context, instance, **kwargs):
"""Deallocate all network resources related to the instance."""
Expand All @@ -329,7 +344,7 @@ def deallocate_for_instance(self, context, instance, **kwargs):
try:
quantumv2.get_client(context).delete_port(port['id'])
except Exception:
LOG.exception(_("Failed to delete quantum port %(portid)s ")
LOG.exception(_("Failed to delete quantum port %(portid)s")
% {'portid': port['id']})

@refresh_cache
Expand Down Expand Up @@ -609,7 +624,7 @@ def get_floating_ip(self, context, id):
return self._format_floating_ip_model(fip, pool_dict, port_dict)

def _get_floating_ip_pools(self, client, project_id=None):
search_opts = {NET_EXTERNAL: True}
search_opts = {constants.NET_EXTERNAL: True}
if project_id:
search_opts.update({'tenant_id': project_id})
data = client.list_networks(**search_opts)
Expand Down Expand Up @@ -677,7 +692,7 @@ def get_vif_by_mac_address(self, context, mac_address):
raise NotImplementedError()

def _get_floating_ip_pool_id_by_name_or_id(self, client, name_or_id):
search_opts = {NET_EXTERNAL: True, 'fields': 'id'}
search_opts = {constants.NET_EXTERNAL: True, 'fields': 'id'}
if uuidutils.is_uuid_like(name_or_id):
search_opts.update({'id': name_or_id})
else:
Expand Down Expand Up @@ -772,9 +787,21 @@ def migrate_instance_start(self, context, instance, migration):

def migrate_instance_finish(self, context, instance, migration):
"""Finish migrating the network of an instance."""
# NOTE(wenjianhn): just pass to make migrate instance doesn't
# raise for now.
pass
if not self._has_port_binding_extension(refresh_cache=True):
return
quantum = quantumv2.get_client(context, admin=True)
search_opts = {'device_id': instance['uuid'],
'tenant_id': instance['project_id']}
data = quantum.list_ports(**search_opts)
ports = data['ports']
for p in ports:
port_req_body = {'port': {'binding:host_id': instance.get('host')}}
try:
quantum.update_port(p['id'], port_req_body)
except Exception as ex:
with excutils.save_and_reraise_exception():
msg = _("Unable to update host of port %s")
LOG.exception(msg, p['id'])

def add_network_to_project(self, context, project_id, network_uuid=None):
"""Force add a network to the project."""
Expand Down
20 changes: 20 additions & 0 deletions nova/network/quantumv2/constants.py
@@ -0,0 +1,20 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4

# Copyright 2013 UnitedStack Inc.
# All Rights Reserved.
#
# 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.
# @author: Yong Sheng Gong, UnitedStack Inc.

NET_EXTERNAL = 'router:external'
PORTBINDING_EXT = 'Port Binding'

0 comments on commit 7de916f

Please sign in to comment.