diff options
| author | Dan Wendlandt <dan@nicira.com> | 2011-07-21 18:56:34 -0700 |
|---|---|---|
| committer | Dan Wendlandt <dan@nicira.com> | 2011-07-21 18:56:34 -0700 |
| commit | 21df6fcc098435d520ff46fff8d4ffdcee3dd3b3 (patch) | |
| tree | 02da9d8efbba72b7bb9ec25cd4ad861e88a293b8 | |
| parent | c5c14a95355cef408e8dcfeb04b9d796be2af564 (diff) | |
| parent | 6e3c492ae878335ea7ef7e00cc9e9abc55b0a0b4 (diff) | |
merge ryu's branch
33 files changed, 452 insertions, 391 deletions
@@ -1,3 +1,4 @@ +Adam Gandelman <adamg@canonical.com> Alex Meade <alex.meade@rackspace.com> Alexander Sakhnov <asakhnov@mirantis.com> Andrey Brindeyev <abrindeyev@griddynamics.com> diff --git a/bin/nova-dhcpbridge b/bin/nova-dhcpbridge index 6d9d85896..325642d52 100755 --- a/bin/nova-dhcpbridge +++ b/bin/nova-dhcpbridge @@ -91,7 +91,7 @@ def init_leases(interface): """Get the list of hosts for an interface.""" ctxt = context.get_admin_context() network_ref = db.network_get_by_bridge(ctxt, interface) - return linux_net.get_dhcp_leases(ctxt, network_ref['id']) + return linux_net.get_dhcp_leases(ctxt, network_ref) def main(): diff --git a/bin/nova-manage b/bin/nova-manage index 2446309e8..b6029bb52 100755 --- a/bin/nova-manage +++ b/bin/nova-manage @@ -479,7 +479,7 @@ class ProjectCommands(object): except db.api.NoMoreNetworks: print _('No more networks available. If this is a new ' 'installation, you need\nto call something like this:\n\n' - ' nova-manage network create 10.0.0.0/8 10 64\n\n') + ' nova-manage network create pvt 10.0.0.0/8 10 64\n\n') except exception.ProcessExecutionError, e: print e print _("The above error may show that the certificate db has not " @@ -500,7 +500,7 @@ class FixedIpCommands(object): if host is None: fixed_ips = db.fixed_ip_get_all(ctxt) else: - fixed_ips = db.fixed_ip_get_all_by_host(ctxt, host) + fixed_ips = db.fixed_ip_get_all_by_instance_host(ctxt, host) except exception.NotFound as ex: print "error: %s" % ex sys.exit(2) @@ -564,17 +564,17 @@ class NetworkCommands(object): """Class for managing networks.""" def create(self, label=None, fixed_range=None, num_networks=None, - network_size=None, vlan_start=None, + network_size=None, multi_host=None, vlan_start=None, vpn_start=None, fixed_range_v6=None, gateway_v6=None, flat_network_bridge=None, bridge_interface=None): """Creates fixed ips for host by range arguments: label, fixed_range, [num_networks=FLAG], - [network_size=FLAG], [vlan_start=FLAG], + [network_size=FLAG], [multi_host=FLAG], [vlan_start=FLAG], [vpn_start=FLAG], [fixed_range_v6=FLAG], [gateway_v6=FLAG], [flat_network_bridge=FLAG], [bridge_interface=FLAG] - If you wish to use a later argument fill in the gaps with 0s - Ex: network create private 10.0.0.0/8 1 15 0 0 0 0 xenbr1 eth1 - network create private 10.0.0.0/8 1 15 + If you wish to use a later argument fill in the gaps with ""s + Ex: network create private 10.0.0.0/8 1 16 T "" "" "" "" xenbr1 eth1 + network create private 10.0.0.0/8 1 16 """ if not label: msg = _('a label (ex: public) is required to create networks.') @@ -589,6 +589,10 @@ class NetworkCommands(object): num_networks = FLAGS.num_networks if not network_size: network_size = FLAGS.network_size + if not multi_host: + multi_host = FLAGS.multi_host + else: + multi_host = multi_host == 'T' if not vlan_start: vlan_start = FLAGS.vlan_start if not vpn_start: @@ -607,6 +611,7 @@ class NetworkCommands(object): net_manager.create_networks(context.get_admin_context(), label=label, cidr=fixed_range, + multi_host=multi_host, num_networks=int(num_networks), network_size=int(network_size), vlan_start=int(vlan_start), diff --git a/nova/api/openstack/consoles.py b/nova/api/openstack/consoles.py index 7a43fba96..9c7b37f0d 100644 --- a/nova/api/openstack/consoles.py +++ b/nova/api/openstack/consoles.py @@ -16,6 +16,7 @@ # under the License. from webob import exc +import webob from nova import console from nova import exception @@ -86,7 +87,7 @@ class Controller(object): int(id)) except exception.NotFound: return faults.Fault(exc.HTTPNotFound()) - return exc.HTTPAccepted() + return webob.Response(status_int=202) def create_resource(): diff --git a/nova/api/openstack/contrib/multinic.py b/nova/api/openstack/contrib/multinic.py index 841061721..da8dcee5d 100644 --- a/nova/api/openstack/contrib/multinic.py +++ b/nova/api/openstack/contrib/multinic.py @@ -16,6 +16,7 @@ """The multinic extension.""" from webob import exc +import webob from nova import compute from nova import log as logging @@ -103,7 +104,7 @@ class Multinic(extensions.ExtensionDescriptor): except Exception, e: LOG.exception(_("Error in addFixedIp %s"), e) return faults.Fault(exc.HTTPBadRequest()) - return exc.HTTPAccepted() + return webob.Response(status_int=202) def _remove_fixed_ip(self, input_dict, req, id): """Removes an IP from an instance.""" @@ -122,4 +123,4 @@ class Multinic(extensions.ExtensionDescriptor): except Exception, e: LOG.exception(_("Error in removeFixedIp %s"), e) return faults.Fault(exc.HTTPBadRequest()) - return exc.HTTPAccepted() + return webob.Response(status_int=202) diff --git a/nova/api/openstack/contrib/vifs.py b/nova/api/openstack/contrib/vifs.py deleted file mode 100644 index a54f412a6..000000000 --- a/nova/api/openstack/contrib/vifs.py +++ /dev/null @@ -1,85 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright (C) 2011 Midokura KK -# -# 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 -from webob import exc - -from nova import exception -from nova import network -from nova.api.openstack import extensions - - -def _translate_vif_view(vif): - result = {'id': vif['id'], - 'mac': vif['address'], - 'network_id': vif['network_id']} - return {'vif': result} - - -def _translate_vifs_view(vifs): - return {'vifs': [_translate_vif_view(vif) for vif in vifs]} - - -class ServerVIFController(object): - """The controller for VIFs attached to servers. - - A child resource of the server. - """ - - _serialization_metadata = { - 'application/xml': { - "attributes": { - "vif": [ - "id", - "mac", - "network_id", - ]}}} - - def __init__(self): - self.network_api = network.API() - super(ServerVIFController, self).__init__() - - def index(self, req, server_id): - """Return data about the vifs of the server.""" - context = req.environ['nova.context'] - - vifs = self.network_api.get_vifs_by_instance(context, server_id) - - return _translate_vifs_view(vifs) - - -class VIFs(extensions.ExtensionDescriptor): - def get_name(self): - return "VIFs" - - def get_alias(self): - return "os-vifs" - - def get_description(self): - return "VIF support" - - def get_namespace(self): - return "http://docs.openstack.org/ext/vifs/api/v1.1" - - def get_updated(self): - return "2011-07-11T00:00:00+00:00" - - def get_resources(self): - resources = [] - - res = extensions.ResourceExtension('os-server_vifs', - ServerVIFController()) - resources.append(res) - - return resources diff --git a/nova/api/openstack/contrib/volumes.py b/nova/api/openstack/contrib/volumes.py index e5e2c5b50..827e36097 100644 --- a/nova/api/openstack/contrib/volumes.py +++ b/nova/api/openstack/contrib/volumes.py @@ -16,6 +16,7 @@ """The volumes extension.""" from webob import exc +import webob from nova import compute from nova import exception @@ -104,7 +105,7 @@ class VolumeController(object): self.volume_api.delete(context, volume_id=id) except exception.NotFound: return faults.Fault(exc.HTTPNotFound()) - return exc.HTTPAccepted() + return webob.Response(status_int=202) def index(self, req): """Returns a summary list of volumes.""" @@ -279,7 +280,7 @@ class VolumeAttachmentController(object): self.compute_api.detach_volume(context, volume_id=volume_id) - return exc.HTTPAccepted() + return webob.Response(status_int=202) def _items(self, req, server_id, entity_maker): """Returns a list of attachments, transformed through entity_maker.""" diff --git a/nova/api/openstack/faults.py b/nova/api/openstack/faults.py index b9a23c126..24cde69e4 100644 --- a/nova/api/openstack/faults.py +++ b/nova/api/openstack/faults.py @@ -40,6 +40,7 @@ class Fault(webob.exc.HTTPException): def __init__(self, exception): """Create a Fault for the given webob.exc.exception.""" self.wrapped_exc = exception + self.status_int = exception.status_int @webob.dec.wsgify(RequestClass=wsgi.Request) def __call__(self, req): diff --git a/nova/api/openstack/servers.py b/nova/api/openstack/servers.py index 93f8e832c..3b3d0685d 100644 --- a/nova/api/openstack/servers.py +++ b/nova/api/openstack/servers.py @@ -17,6 +17,7 @@ import base64 import traceback from webob import exc +import webob from nova import compute from nova import db @@ -189,7 +190,7 @@ class Controller(object): except Exception, e: LOG.exception(_("Error in revert-resize %s"), e) return faults.Fault(exc.HTTPBadRequest()) - return exc.HTTPAccepted() + return webob.Response(status_int=202) def _action_resize(self, input_dict, req, id): return exc.HTTPNotImplemented() @@ -207,7 +208,7 @@ class Controller(object): except Exception, e: LOG.exception(_("Error in reboot %s"), e) return faults.Fault(exc.HTTPUnprocessableEntity()) - return exc.HTTPAccepted() + return webob.Response(status_int=202) def _action_migrate(self, input_dict, req, id): try: @@ -215,7 +216,7 @@ class Controller(object): except Exception, e: LOG.exception(_("Error in migrate %s"), e) return faults.Fault(exc.HTTPBadRequest()) - return exc.HTTPAccepted() + return webob.Response(status_int=202) @scheduler_api.redirect_handler def lock(self, req, id): @@ -231,7 +232,7 @@ class Controller(object): readable = traceback.format_exc() LOG.exception(_("Compute.api::lock %s"), readable) return faults.Fault(exc.HTTPUnprocessableEntity()) - return exc.HTTPAccepted() + return webob.Response(status_int=202) @scheduler_api.redirect_handler def unlock(self, req, id): @@ -247,7 +248,7 @@ class Controller(object): readable = traceback.format_exc() LOG.exception(_("Compute.api::unlock %s"), readable) return faults.Fault(exc.HTTPUnprocessableEntity()) - return exc.HTTPAccepted() + return webob.Response(status_int=202) @scheduler_api.redirect_handler def get_lock(self, req, id): @@ -262,7 +263,7 @@ class Controller(object): readable = traceback.format_exc() LOG.exception(_("Compute.api::get_lock %s"), readable) return faults.Fault(exc.HTTPUnprocessableEntity()) - return exc.HTTPAccepted() + return webob.Response(status_int=202) @scheduler_api.redirect_handler def reset_network(self, req, id, body): @@ -277,7 +278,7 @@ class Controller(object): readable = traceback.format_exc() LOG.exception(_("Compute.api::reset_network %s"), readable) return faults.Fault(exc.HTTPUnprocessableEntity()) - return exc.HTTPAccepted() + return webob.Response(status_int=202) @scheduler_api.redirect_handler def inject_network_info(self, req, id, body): @@ -292,7 +293,7 @@ class Controller(object): readable = traceback.format_exc() LOG.exception(_("Compute.api::inject_network_info %s"), readable) return faults.Fault(exc.HTTPUnprocessableEntity()) - return exc.HTTPAccepted() + return webob.Response(status_int=202) @scheduler_api.redirect_handler def pause(self, req, id, body): @@ -304,7 +305,7 @@ class Controller(object): readable = traceback.format_exc() LOG.exception(_("Compute.api::pause %s"), readable) return faults.Fault(exc.HTTPUnprocessableEntity()) - return exc.HTTPAccepted() + return webob.Response(status_int=202) @scheduler_api.redirect_handler def unpause(self, req, id, body): @@ -316,7 +317,7 @@ class Controller(object): readable = traceback.format_exc() LOG.exception(_("Compute.api::unpause %s"), readable) return faults.Fault(exc.HTTPUnprocessableEntity()) - return exc.HTTPAccepted() + return webob.Response(status_int=202) @scheduler_api.redirect_handler def suspend(self, req, id, body): @@ -328,7 +329,7 @@ class Controller(object): readable = traceback.format_exc() LOG.exception(_("compute.api::suspend %s"), readable) return faults.Fault(exc.HTTPUnprocessableEntity()) - return exc.HTTPAccepted() + return webob.Response(status_int=202) @scheduler_api.redirect_handler def resume(self, req, id, body): @@ -340,7 +341,7 @@ class Controller(object): readable = traceback.format_exc() LOG.exception(_("compute.api::resume %s"), readable) return faults.Fault(exc.HTTPUnprocessableEntity()) - return exc.HTTPAccepted() + return webob.Response(status_int=202) @scheduler_api.redirect_handler def rescue(self, req, id): @@ -352,7 +353,7 @@ class Controller(object): readable = traceback.format_exc() LOG.exception(_("compute.api::rescue %s"), readable) return faults.Fault(exc.HTTPUnprocessableEntity()) - return exc.HTTPAccepted() + return webob.Response(status_int=202) @scheduler_api.redirect_handler def unrescue(self, req, id): @@ -364,7 +365,7 @@ class Controller(object): readable = traceback.format_exc() LOG.exception(_("compute.api::unrescue %s"), readable) return faults.Fault(exc.HTTPUnprocessableEntity()) - return exc.HTTPAccepted() + return webob.Response(status_int=202) @scheduler_api.redirect_handler def get_ajax_console(self, req, id): @@ -374,7 +375,7 @@ class Controller(object): int(id)) except exception.NotFound: return faults.Fault(exc.HTTPNotFound()) - return exc.HTTPAccepted() + return webob.Response(status_int=202) @scheduler_api.redirect_handler def get_vnc_console(self, req, id): @@ -384,7 +385,7 @@ class Controller(object): int(id)) except exception.NotFound: return faults.Fault(exc.HTTPNotFound()) - return exc.HTTPAccepted() + return webob.Response(status_int=202) @scheduler_api.redirect_handler def diagnostics(self, req, id): @@ -416,7 +417,7 @@ class ControllerV10(Controller): self.compute_api.delete(req.environ['nova.context'], id) except exception.NotFound: return faults.Fault(exc.HTTPNotFound()) - return exc.HTTPAccepted() + return webob.Response(status_int=202) def _image_ref_from_req_data(self, data): return data['server']['imageId'] @@ -450,7 +451,7 @@ class ControllerV10(Controller): except Exception, e: LOG.exception(_("Error in resize %s"), e) return faults.Fault(exc.HTTPBadRequest()) - return exc.HTTPAccepted() + return webob.Response(status_int=202) def _action_rebuild(self, info, request, instance_id): context = request.environ['nova.context'] @@ -470,9 +471,7 @@ class ControllerV10(Controller): LOG.debug(msg) return faults.Fault(exc.HTTPConflict(explanation=msg)) - response = exc.HTTPAccepted() - response.empty_body = True - return response + return webob.Response(status_int=202) def _get_server_admin_password(self, server): """ Determine the admin password for a server on creation """ @@ -519,7 +518,7 @@ class ControllerV11(Controller): msg = _("Invalid adminPass") return exc.HTTPBadRequest(explanation=msg) self.compute_api.set_admin_password(context, id, password) - return exc.HTTPAccepted() + return webob.Response(status_int=202) def _limit_items(self, items, req): return common.limited_by_marker(items, req) @@ -565,7 +564,7 @@ class ControllerV11(Controller): except Exception, e: LOG.exception(_("Error in resize %s"), e) return faults.Fault(exc.HTTPBadRequest()) - return exc.HTTPAccepted() + return webob.Response(status_int=202) def _action_rebuild(self, info, request, instance_id): context = request.environ['nova.context'] @@ -594,9 +593,7 @@ class ControllerV11(Controller): LOG.debug(msg) return faults.Fault(exc.HTTPConflict(explanation=msg)) - response = exc.HTTPAccepted() - response.empty_body = True - return response + return webob.Response(status_int=202) def get_default_xmlns(self, req): return common.XML_NS_V11 diff --git a/nova/compute/manager.py b/nova/compute/manager.py index 367ac4a8d..e60781c6d 100644 --- a/nova/compute/manager.py +++ b/nova/compute/manager.py @@ -77,8 +77,6 @@ flags.DEFINE_integer('live_migration_retry_count', 30, flags.DEFINE_integer("rescue_timeout", 0, "Automatically unrescue an instance after N seconds." " Set to 0 to disable.") -flags.DEFINE_bool('auto_assign_floating_ip', False, - 'Autoassigning floating ip to VM') flags.DEFINE_integer('host_state_interval', 120, 'Interval in seconds for querying the host status') @@ -283,16 +281,19 @@ class ComputeManager(manager.SchedulerDependentManager): """Launch a new instance with specified options.""" context = context.elevated() instance = self.db.instance_get(context, instance_id) - instance.injected_files = kwargs.get('injected_files', []) - instance.admin_pass = kwargs.get('admin_password', None) if instance['name'] in self.driver.list_instances(): raise exception.Error(_("Instance has already been created")) LOG.audit(_("instance %s: starting..."), instance_id, context=context) - self.db.instance_update(context, - instance_id, - {'host': self.host, 'launched_on': self.host}) - + updates = {} + updates['host'] = self.host + updates['launched_on'] = self.host + # NOTE(vish): used by virt but not in database + updates['injected_files'] = kwargs.get('injected_files', []) + updates['admin_pass'] = kwargs.get('admin_password', None) + instance = self.db.instance_update(context, + instance_id, + updates) self.db.instance_set_state(context, instance_id, power_state.NOSTATE, diff --git a/nova/db/api.py b/nova/db/api.py index cb4da169c..2efbf957d 100644 --- a/nova/db/api.py +++ b/nova/db/api.py @@ -332,13 +332,14 @@ def fixed_ip_associate(context, address, instance_id): return IMPL.fixed_ip_associate(context, address, instance_id) -def fixed_ip_associate_pool(context, network_id, instance_id): - """Find free ip in network and associate it to instance. +def fixed_ip_associate_pool(context, network_id, instance_id=None, host=None): + """Find free ip in network and associate it to instance or host. Raises if one is not available. """ - return IMPL.fixed_ip_associate_pool(context, network_id, instance_id) + return IMPL.fixed_ip_associate_pool(context, network_id, + instance_id, host) def fixed_ip_create(context, values): @@ -361,9 +362,9 @@ def fixed_ip_get_all(context): return IMPL.fixed_ip_get_all(context) -def fixed_ip_get_all_by_host(context, host): - """Get all defined fixed ips used by a host.""" - return IMPL.fixed_ip_get_all_by_host(context, host) +def fixed_ip_get_all_by_instance_host(context, host): + """Get all allocated fixed ips filtered by instance host.""" + return IMPL.fixed_ip_get_all_instance_by_host(context, host) def fixed_ip_get_by_address(context, address): @@ -376,6 +377,11 @@ def fixed_ip_get_by_instance(context, instance_id): return IMPL.fixed_ip_get_by_instance(context, instance_id) +def fixed_ip_get_by_network_host(context, network_id, host): + """Get fixed ip for a host in a network.""" + return IMPL.fixed_ip_get_by_network_host(context, network_id, host) + + def fixed_ip_get_by_virtual_interface(context, vif_id): """Get fixed ips by virtual interface or raise if none exist.""" return IMPL.fixed_ip_get_by_virtual_interface(context, vif_id) diff --git a/nova/db/sqlalchemy/api.py b/nova/db/sqlalchemy/api.py index 189be0714..33bdd767a 100644 --- a/nova/db/sqlalchemy/api.py +++ b/nova/db/sqlalchemy/api.py @@ -18,7 +18,6 @@ """ Implementation of SQLAlchemy backend. """ -import traceback import warnings from nova import db @@ -33,7 +32,6 @@ from sqlalchemy import or_ from sqlalchemy.exc import IntegrityError from sqlalchemy.orm import joinedload from sqlalchemy.orm import joinedload_all -from sqlalchemy.sql import exists from sqlalchemy.sql import func from sqlalchemy.sql.expression import literal_column @@ -672,7 +670,7 @@ def fixed_ip_associate(context, address, instance_id): @require_admin_context -def fixed_ip_associate_pool(context, network_id, instance_id): +def fixed_ip_associate_pool(context, network_id, instance_id=None, host=None): session = get_session() with session.begin(): network_or_none = or_(models.FixedIp.network_id == network_id, @@ -682,6 +680,7 @@ def fixed_ip_associate_pool(context, network_id, instance_id): filter_by(reserved=False).\ filter_by(deleted=False).\ filter_by(instance=None).\ + filter_by(host=None).\ with_lockmode('update').\ first() # NOTE(vish): if with_lockmode isn't supported, as in sqlite, @@ -692,9 +691,12 @@ def fixed_ip_associate_pool(context, network_id, instance_id): fixed_ip_ref.network = network_get(context, network_id, session=session) - fixed_ip_ref.instance = instance_get(context, - instance_id, - session=session) + if instance_id: + fixed_ip_ref.instance = instance_get(context, + instance_id, + session=session) + if host: + fixed_ip_ref.host = host session.add(fixed_ip_ref) return fixed_ip_ref['address'] @@ -750,7 +752,7 @@ def fixed_ip_get_all(context, session=None): @require_admin_context -def fixed_ip_get_all_by_host(context, host=None): +def fixed_ip_get_all_by_instance_host(context, host=None): session = get_session() result = session.query(models.FixedIp).\ @@ -800,6 +802,20 @@ def fixed_ip_get_by_instance(context, instance_id): @require_context +def fixed_ip_get_by_network_host(context, network_id, host): + session = get_session() + rv = session.query(models.FixedIp).\ + filter_by(network_id=network_id).\ + filter_by(host=host).\ + filter_by(deleted=False).\ + first() + if not rv: + raise exception.FixedIpNotFoundForNetworkHost(network_id=network_id, + host=host) + return rv + + +@require_context def fixed_ip_get_by_virtual_interface(context, vif_id): session = get_session() rv = session.query(models.FixedIp).\ @@ -1480,8 +1496,6 @@ def network_associate(context, project_id, force=False): called by project_get_networks under certain conditions and network manager add_network_to_project() - only associates projects with networks that have configured hosts - only associate if the project doesn't already have a network or if force is True @@ -1497,7 +1511,6 @@ def network_associate(context, project_id, force=False): def network_query(project_filter): return session.query(models.Network).\ filter_by(deleted=False).\ - filter(models.Network.host != None).\ filter_by(project_id=project_filter).\ with_lockmode('update').\ first() @@ -1704,9 +1717,16 @@ def network_get_all_by_instance(_context, instance_id): def network_get_all_by_host(context, host): session = get_session() with session.begin(): + # NOTE(vish): return networks that have host set + # or that have a fixed ip with host set + host_filter = or_(models.Network.host == host, + models.FixedIp.host == host) + return session.query(models.Network).\ filter_by(deleted=False).\ - filter_by(host=host).\ + join(models.Network.fixed_ips).\ + filter(host_filter).\ + filter_by(deleted=False).\ all() @@ -1738,6 +1758,7 @@ def network_update(context, network_id, values): network_ref = network_get(context, network_id, session=session) network_ref.update(values) network_ref.save(session=session) + return network_ref ################### diff --git a/nova/db/sqlalchemy/migrate_repo/versions/033_ha_network.py b/nova/db/sqlalchemy/migrate_repo/versions/033_ha_network.py new file mode 100644 index 000000000..3a5f7eba8 --- /dev/null +++ b/nova/db/sqlalchemy/migrate_repo/versions/033_ha_network.py @@ -0,0 +1,44 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright (c) 2011 OpenStack, LLC. +# 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. + +from sqlalchemy import Column, Table, MetaData, Boolean, String + +meta = MetaData() + +fixed_ips_host = Column('host', String(255)) + +networks_multi_host = Column('multi_host', Boolean, default=False) + + +def upgrade(migrate_engine): + meta.bind = migrate_engine + + fixed_ips = Table('fixed_ips', meta, autoload=True) + fixed_ips.create_column(fixed_ips_host) + + networks = Table('networks', meta, autoload=True) + networks.create_column(networks_multi_host) + + +def downgrade(migrate_engine): + meta.bind = migrate_engine + + fixed_ips = Table('fixed_ips', meta, autoload=True) + fixed_ips.drop_column(fixed_ips_host) + + networks = Table('networks', meta, autoload=True) + networks.drop_column(networks_multi_host) diff --git a/nova/db/sqlalchemy/models.py b/nova/db/sqlalchemy/models.py index 1bcc8eaec..e42f605c4 100644 --- a/nova/db/sqlalchemy/models.py +++ b/nova/db/sqlalchemy/models.py @@ -545,6 +545,7 @@ class Network(BASE, NovaBase): injected = Column(Boolean, default=False) cidr = Column(String(255), unique=True) cidr_v6 = Column(String(255), unique=True) + multi_host = Column(Boolean, default=False) gateway_v6 = Column(String(255)) netmask_v6 = Column(String(255)) @@ -603,6 +604,7 @@ class FixedIp(BASE, NovaBase): # leased means dhcp bridge has leased the ip leased = Column(Boolean, default=False) reserved = Column(Boolean, default=False) + host = Column(String(255)) class FloatingIp(BASE, NovaBase): diff --git a/nova/exception.py b/nova/exception.py index ad6c005f8..cb015e694 100644 --- a/nova/exception.py +++ b/nova/exception.py @@ -408,6 +408,11 @@ class FixedIpNotFoundForInstance(FixedIpNotFound): message = _("Instance %(instance_id)s has zero fixed ips.") +class FixedIpNotFoundForNetworkHost(FixedIpNotFound): + message = _("Network host %(host)s has zero fixed ips " + "in network %(network_id)s.") + + class FixedIpNotFoundForSpecificInstance(FixedIpNotFound): message = _("Instance %(instance_id)s doesn't have fixed ip '%(ip)s'.") diff --git a/nova/network/api.py b/nova/network/api.py index 4b8e9fbc6..33a9fe239 100644 --- a/nova/network/api.py +++ b/nova/network/api.py @@ -18,7 +18,6 @@ """Handles all requests relating to instances (guest vms).""" -from nova import db from nova import exception from nova import flags from nova import log as logging @@ -112,7 +111,11 @@ class API(base.Base): '(%(project)s)') % {'address': floating_ip['address'], 'project': context.project_id}) - host = fixed_ip['network']['host'] + # NOTE(vish): if we are multi_host, send to the instances host + if fixed_ip['network']['multi_host']: + host = fixed_ip['instance']['host'] + else: + host = fixed_ip['network']['host'] rpc.cast(context, self.db.queue_get_for(context, FLAGS.network_topic, host), {'method': 'associate_floating_ip', @@ -127,7 +130,11 @@ class API(base.Base): return if not floating_ip.get('fixed_ip'): raise exception.ApiError('Address is not associated.') - host = floating_ip['fixed_ip']['network']['host'] + # NOTE(vish): if we are multi_host, send to the instances host + if floating_ip['fixed_ip']['network']['multi_host']: + host = floating_ip['fixed_ip']['instance']['host'] + else: + host = floating_ip['fixed_ip']['network']['host'] rpc.call(context, self.db.queue_get_for(context, FLAGS.network_topic, host), {'method': 'disassociate_floating_ip', @@ -141,7 +148,9 @@ class API(base.Base): args = kwargs args['instance_id'] = instance['id'] args['project_id'] = instance['project_id'] + args['host'] = instance['host'] args['instance_type_id'] = instance['instance_type_id'] + return rpc.call(context, FLAGS.network_topic, {'method': 'allocate_for_instance', 'args': args}) @@ -180,7 +189,8 @@ class API(base.Base): def get_instance_nw_info(self, context, instance): """Returns all network info related to an instance.""" args = {'instance_id': instance['id'], - 'instance_type_id': instance['instance_type_id']} + 'instance_type_id': instance['instance_type_id'], + 'host': instance['host']} return rpc.call(context, FLAGS.network_topic, {'method': 'get_instance_nw_info', 'args': args}) diff --git a/nova/network/linux_net.py b/nova/network/linux_net.py index 4e39a57c4..8ace07884 100644 --- a/nova/network/linux_net.py +++ b/nova/network/linux_net.py @@ -498,7 +498,7 @@ def ensure_bridge(bridge, interface, net_attrs=None): suffix = net_attrs['cidr'].rpartition('/')[2] out, err = _execute('sudo', 'ip', 'addr', 'add', '%s/%s' % - (net_attrs['gateway'], suffix), + (net_attrs['dhcp_server'], suffix), 'brd', net_attrs['broadcast'], 'dev', @@ -552,21 +552,27 @@ def ensure_bridge(bridge, interface, net_attrs=None): bridge) -def get_dhcp_leases(context, network_id): +def get_dhcp_leases(context, network_ref): """Return a network's hosts config in dnsmasq leasefile format.""" hosts = [] - for fixed_ip_ref in db.network_get_associated_fixed_ips(context, - network_id): - hosts.append(_host_lease(fixed_ip_ref)) + for fixed_ref in db.network_get_associated_fixed_ips(context, + network_ref['id']): + host = fixed_ref['instance']['host'] + if network_ref['multi_host'] and FLAGS.host != host: + continue + hosts.append(_host_lease(fixed_ref)) return '\n'.join(hosts) -def get_dhcp_hosts(context, network_id): +def get_dhcp_hosts(context, network_ref): """Get network's hosts config in dhcp-host format.""" hosts = [] - for fixed_ip_ref in db.network_get_associated_fixed_ips(context, - network_id): - hosts.append(_host_dhcp(fixed_ip_ref)) + for fixed_ref in db.network_get_associated_fixed_ips(context, + network_ref['id']): + host = fixed_ref['instance']['host'] + if network_ref['multi_host'] and FLAGS.host != host: + continue + hosts.append(_host_dhcp(fixed_ref)) return '\n'.join(hosts) @@ -574,18 +580,16 @@ def get_dhcp_hosts(context, network_id): # configuration options (like dchp-range, vlan, ...) # aren't reloaded. @utils.synchronized('dnsmasq_start') -def update_dhcp(context, network_id): +def update_dhcp(context, network_ref): """(Re)starts a dnsmasq server for a given network. If a dnsmasq instance is already running then send a HUP signal causing it to reload, otherwise spawn a new instance. """ - network_ref = db.network_get(context, network_id) - conffile = _dhcp_file(network_ref['bridge'], 'conf') with open(conffile, 'w') as f: - f.write(get_dhcp_hosts(context, network_id)) + f.write(get_dhcp_hosts(context, network_ref)) # Make sure dnsmasq can actually read it (it setuid()s to "nobody") os.chmod(conffile, 0644) @@ -613,9 +617,7 @@ def update_dhcp(context, network_id): @utils.synchronized('radvd_start') -def update_ra(context, network_id): - network_ref = db.network_get(context, network_id) - +def update_ra(context, network_ref): conffile = _ra_file(network_ref['bridge'], 'conf') with open(conffile, 'w') as f: conf_str = """ @@ -651,9 +653,6 @@ interface %s LOG.debug(_('Pid %d is stale, relaunching radvd'), pid) command = _ra_cmd(network_ref) _execute(*command) - db.network_update(context, network_id, - {'gateway_v6': - utils.get_my_linklocal(network_ref['bridge'])}) def _host_lease(fixed_ip_ref): @@ -702,10 +701,11 @@ def _dnsmasq_cmd(net): cmd = ['sudo', '-E', 'dnsmasq', '--strict-order', '--bind-interfaces', + '--interface=%s' % net['bridge'], '--conf-file=%s' % FLAGS.dnsmasq_config_file, '--domain=%s' % FLAGS.dhcp_domain, '--pid-file=%s' % _dhcp_file(net['bridge'], 'pid'), - '--listen-address=%s' % net['gateway'], + '--listen-address=%s' % net['dhcp_server'], '--except-interface=lo', '--dhcp-range=%s,static,120s' % net['dhcp_start'], '--dhcp-lease-max=%s' % len(netaddr.IPNetwork(net['cidr'])), diff --git a/nova/network/manager.py b/nova/network/manager.py index 827fd04e8..83d0519c9 100644 --- a/nova/network/manager.py +++ b/nova/network/manager.py @@ -28,7 +28,6 @@ topologies. All of the network commands are issued to a subclass of :flat_network_bridge: Bridge device for simple network instances :flat_interface: FlatDhcp will bridge into this interface if set :flat_network_dns: Dns for simple network -:flat_network_dhcp_start: Dhcp start for FlatDhcp :vlan_start: First VLAN for private networks :vpn_ip: Public IP for the cloudpipe VPN servers :vpn_start: First Vpn port for private networks @@ -49,7 +48,6 @@ import datetime import math import netaddr import socket -import pickle from eventlet import greenpool from nova import context @@ -78,8 +76,6 @@ flags.DEFINE_bool('flat_injected', True, 'Whether to attempt to inject network setup into guest') flags.DEFINE_string('flat_interface', None, 'FlatDhcp will bridge into this interface if set') -flags.DEFINE_string('flat_network_dhcp_start', '10.0.0.2', - 'Dhcp start for FlatDhcp') flags.DEFINE_integer('vlan_start', 100, 'First VLAN for private networks') flags.DEFINE_string('vlan_interface', None, 'vlans will bridge into this interface if set') @@ -87,6 +83,8 @@ flags.DEFINE_integer('num_networks', 1, 'Number of networks to support') flags.DEFINE_string('vpn_ip', '$my_ip', 'Public IP for the cloudpipe VPN servers') flags.DEFINE_integer('vpn_start', 1000, 'First Vpn port for private networks') +flags.DEFINE_bool('multi_host', False, + 'Default value for multi_host in networks') flags.DEFINE_integer('network_size', 256, 'Number of addresses in each private subnet') flags.DEFINE_string('floating_range', '4.4.4.0/24', @@ -104,7 +102,8 @@ flags.DEFINE_integer('fixed_ip_disassociate_timeout', 600, 'Seconds after which a deallocated ip is disassociated') flags.DEFINE_integer('create_unique_mac_address_attempts', 5, 'Number of attempts to create unique mac address') - +flags.DEFINE_bool('auto_assign_floating_ip', False, + 'Autoassigning floating ip to VM') flags.DEFINE_bool('use_ipv6', False, 'use the ipv6') flags.DEFINE_string('network_host', socket.gethostname(), @@ -124,16 +123,26 @@ class RPCAllocateFixedIP(object): used since they share code to RPC.call allocate_fixed_ip on the correct network host to configure dnsmasq """ - def _allocate_fixed_ips(self, context, instance_id, networks, **kwargs): + def _allocate_fixed_ips(self, context, instance_id, host, networks, + **kwargs): """Calls allocate_fixed_ip once for each network.""" green_pool = greenpool.GreenPool() vpn = kwargs.pop('vpn') for network in networks: - if network['host'] != self.host: + # NOTE(vish): if we are not multi_host pass to the network host + if not network['multi_host']: + host = network['host'] + # NOTE(vish): if there is no network host, set one + if host == None: + host = rpc.call(context, FLAGS.network_topic, + {'method': 'set_network_host', + 'args': {'network_ref': network}}) + if host != self.host: # need to call allocate_fixed_ip to correct network host - topic = self.db.queue_get_for(context, FLAGS.network_topic, - network['host']) + topic = self.db.queue_get_for(context, + FLAGS.network_topic, + host) args = {} args['instance_id'] = instance_id args['network_id'] = network['id'] @@ -149,12 +158,13 @@ class RPCAllocateFixedIP(object): # wait for all of the allocates (if any) to finish green_pool.waitall() - def _rpc_allocate_fixed_ip(self, context, instance_id, network_id): + def _rpc_allocate_fixed_ip(self, context, instance_id, network_id, + **kwargs): """Sits in between _allocate_fixed_ips and allocate_fixed_ip to perform network lookup on the far side of rpc. """ network = self.db.network_get(context, network_id) - self.allocate_fixed_ip(context, instance_id, network) + self.allocate_fixed_ip(context, instance_id, network, **kwargs) class FloatingIP(object): @@ -193,7 +203,7 @@ class FloatingIP(object): # which is currently the NetworkManager version # do this first so fixed ip is already allocated ips = super(FloatingIP, self).allocate_for_instance(context, **kwargs) - if hasattr(FLAGS, 'auto_assign_floating_ip'): + if FLAGS.auto_assign_floating_ip: # allocate a floating ip (public_ip is just the address string) public_ip = self.allocate_floating_ip(context, project_id) # set auto_assigned column to true for the floating ip @@ -300,15 +310,36 @@ class NetworkManager(manager.SchedulerDependentManager): super(NetworkManager, self).__init__(service_name='network', *args, **kwargs) + @utils.synchronized('get_dhcp') + def _get_dhcp_ip(self, context, network_ref, host=None): + """Get the proper dhcp address to listen on.""" + # NOTE(vish): this is for compatibility + if not network_ref['multi_host']: + return network_ref['gateway'] + + if not host: + host = self.host + network_id = network_ref['id'] + try: + fip = self.db.fixed_ip_get_by_network_host(context, + network_id, + host) + return fip['address'] + except exception.FixedIpNotFoundForNetworkHost: + elevated = context.elevated() + return self.db.fixed_ip_associate_pool(elevated, + network_id, + host=host) + def init_host(self): """Do any initialization that needs to be run if this is a standalone service. """ - # Set up this host for networks in which it's already - # the designated network host. + # NOTE(vish): Set up networks for which this host already has + # an ip address. ctxt = context.get_admin_context() for network in self.db.network_get_all_by_host(ctxt, self.host): - self._on_set_network_host(ctxt, network['id']) + self._setup_network(ctxt, network) def periodic_tasks(self, context=None): """Tasks to be run at a periodic interval.""" @@ -323,32 +354,13 @@ class NetworkManager(manager.SchedulerDependentManager): if num: LOG.debug(_('Dissassociated %s stale fixed ip(s)'), num) - # setup any new networks which have been created - self.set_network_hosts(context) - - def set_network_host(self, context, network_id): + def set_network_host(self, context, network_ref): """Safely sets the host of the network.""" LOG.debug(_('setting network host'), context=context) host = self.db.network_set_host(context, - network_id, + network_ref['id'], self.host) - if host == self.host: - self._on_set_network_host(context, network_id) - - def set_network_hosts(self, context): - """Set the network hosts for any networks which are unset.""" - try: - networks = self.db.network_get_all(context) - except exception.NoNetworksFound: - # no networks found, nothing to do - return - - for network in networks: - host = network['host'] - if not host: - # break so worker will only grab 1 (to help scale flatter) - self.set_network_host(context, network['id']) - break + return host def _get_networks_for_instance(self, context, instance_id, project_id): """Determine & return which networks an instance should connect to.""" @@ -361,9 +373,9 @@ class NetworkManager(manager.SchedulerDependentManager): # we don't care if no networks are found pass - # return only networks which are not vlan networks and have host set + # return only networks which are not vlan networks return [network for network in networks if - not network['vlan'] and network['host']] + not network['vlan']] def allocate_for_instance(self, context, **kwargs): """Handles allocating the various network resources for an instance. @@ -371,6 +383,7 @@ class NetworkManager(manager.SchedulerDependentManager): rpc.called by network_api """ instance_id = kwargs.pop('instance_id') + host = kwargs.pop('host') project_id = kwargs.pop('project_id') type_id = kwargs.pop('instance_type_id') vpn = kwargs.pop('vpn') @@ -379,9 +392,11 @@ class NetworkManager(manager.SchedulerDependentManager): context=context) networks = self._get_networks_for_instance(admin_context, instance_id, project_id) + LOG.warn(networks) self._allocate_mac_addresses(context, instance_id, networks) - self._allocate_fixed_ips(admin_context, instance_id, networks, vpn=vpn) - return self.get_instance_nw_info(context, instance_id, type_id) + self._allocate_fixed_ips(admin_context, instance_id, host, networks, + vpn=vpn) + return self.get_instance_nw_info(context, instance_id, type_id, host) def deallocate_for_instance(self, context, **kwargs): """Handles deallocating various network resources for an instance. @@ -401,7 +416,8 @@ class NetworkManager(manager.SchedulerDependentManager): # deallocate vifs (mac addresses) self.db.virtual_interface_delete_by_instance(context, instance_id) - def get_instance_nw_info(self, context, instance_id, instance_type_id): + def get_instance_nw_info(self, context, instance_id, + instance_type_id, host): """Creates network info list for instance. called by allocate_for_instance and netowrk_api @@ -411,7 +427,12 @@ class NetworkManager(manager.SchedulerDependentManager): and info = dict containing pertinent networking data """ # TODO(tr3buchet) should handle floating IPs as well? - fixed_ips = self.db.fixed_ip_get_by_instance(context, instance_id) + try: + fixed_ips = self.db.fixed_ip_get_by_instance(context, instance_id) + except exception.FixedIpNotFoundForInstance, ex: + LOG.warn(_('No fixed IPs for instance %s'), instance_id) + fixed_ips = [] + vifs = self.db.virtual_interface_get_by_instance(context, instance_id) flavor = self.db.instance_type_get_by_id(context, instance_type_id) @@ -446,15 +467,25 @@ class NetworkManager(manager.SchedulerDependentManager): 'cidr_v6': network['cidr_v6'], 'injected': network['injected'], 'vlan': network['vlan'], - 'bridge_interface': network['bridge_interface']} + 'bridge_interface': network['bridge_interface'], + 'multi_host': network['multi_host']} + if network['multi_host']: + dhcp_server = self._get_dhcp_ip(context, network, host) + else: + dhcp_server = self._get_dhcp_ip(context, + network, + network['host']) info = { 'label': network['label'], 'gateway': network['gateway'], + 'dhcp_server': dhcp_server, 'broadcast': network['broadcast'], 'mac': vif['address'], 'rxtx_cap': flavor['rxtx_cap'], 'dns': [network['dns']], - 'ips': [ip_dict(ip) for ip in network_IPs]} + 'ips': [ip_dict(ip) for ip in network_IPs], + 'create_bridge': self._create_bridge, + 'create_vlan': self._create_vlan} if network['cidr_v6']: info['ip6s'] = [ip6_dict()] # TODO(tr3buchet): handle ip6 routes here as well @@ -489,10 +520,10 @@ class NetworkManager(manager.SchedulerDependentManager): random.randint(0x00, 0xff)] return ':'.join(map(lambda x: "%02x" % x, mac)) - def add_fixed_ip_to_instance(self, context, instance_id, network_id): + def add_fixed_ip_to_instance(self, context, instance_id, host, network_id): """Adds a fixed ip to an instance from specified network.""" networks = [self.db.network_get(context, network_id)] - self._allocate_fixed_ips(context, instance_id, networks) + self._allocate_fixed_ips(context, instance_id, host, networks) def remove_fixed_ip_from_instance(self, context, instance_id, address): """Removes a fixed ip from an instance from specified network.""" @@ -519,6 +550,7 @@ class NetworkManager(manager.SchedulerDependentManager): values = {'allocated': True, 'virtual_interface_id': vif['id']} self.db.fixed_ip_update(context, address, values) + self._setup_network(context, network) return address def deallocate_fixed_ip(self, context, address, **kwargs): @@ -564,10 +596,10 @@ class NetworkManager(manager.SchedulerDependentManager): # means there will stale entries in the conf file # the code below will update the file if necessary if FLAGS.update_dhcp_on_disassociate: - network = self.db.fixed_ip_get_network(context, address) - self.driver.update_dhcp(context, network['id']) + network_ref = self.db.fixed_ip_get_network(context, address) + self._setup_network(context, network_ref) - def create_networks(self, context, label, cidr, num_networks, + def create_networks(self, context, label, cidr, multi_host, num_networks, network_size, cidr_v6, gateway_v6, bridge, bridge_interface, **kwargs): """Create networks based on parameters.""" @@ -586,6 +618,7 @@ class NetworkManager(manager.SchedulerDependentManager): net['bridge_interface'] = bridge_interface net['dns'] = FLAGS.flat_network_dns net['cidr'] = cidr + net['multi_host'] = multi_host net['netmask'] = str(project_net.netmask) net['gateway'] = str(project_net[1]) net['broadcast'] = str(project_net.broadcast) @@ -661,14 +694,25 @@ class NetworkManager(manager.SchedulerDependentManager): 'address': address, 'reserved': reserved}) - def _allocate_fixed_ips(self, context, instance_id, networks, **kwargs): + def _allocate_fixed_ips(self, context, instance_id, host, networks, + **kwargs): """Calls allocate_fixed_ip once for each network.""" raise NotImplementedError() - def _on_set_network_host(self, context, network_id): - """Called when this host becomes the host for a network.""" + def _setup_network(self, context, network_ref): + """Sets up network on this host.""" raise NotImplementedError() + @property + def _create_bridge(self): + """Indicate whether this manager requires VIF to create a bridge.""" + return False + + @property + def _create_vlan(self): + """Indicate whether this manager requires VIF to create a VLAN tag.""" + return False + class FlatManager(NetworkManager): """Basic network where no vlans are used. @@ -700,7 +744,8 @@ class FlatManager(NetworkManager): timeout_fixed_ips = False - def _allocate_fixed_ips(self, context, instance_id, networks, **kwargs): + def _allocate_fixed_ips(self, context, instance_id, host, networks, + **kwargs): """Calls allocate_fixed_ip once for each network.""" for network in networks: self.allocate_fixed_ip(context, instance_id, network) @@ -711,12 +756,12 @@ class FlatManager(NetworkManager): **kwargs) self.db.fixed_ip_disassociate(context, address) - def _on_set_network_host(self, context, network_id): - """Called when this host becomes the host for a network.""" + def _setup_network(self, context, network_ref): + """Setup Network on this host.""" net = {} net['injected'] = FLAGS.flat_injected net['dns'] = FLAGS.flat_network_dns - self.db.network_update(context, network_id, net) + self.db.network_update(context, network_ref['id'], net) class FlatDHCPManager(FloatingIP, RPCAllocateFixedIP, NetworkManager): @@ -740,27 +785,24 @@ class FlatDHCPManager(FloatingIP, RPCAllocateFixedIP, NetworkManager): self.driver.metadata_forward() - def allocate_fixed_ip(self, context, instance_id, network, **kwargs): - """Allocate flat_network fixed_ip, then setup dhcp for this network.""" - address = super(FlatDHCPManager, self).allocate_fixed_ip(context, - instance_id, - network) + def _setup_network(self, context, network_ref): + """Sets up network on this host.""" + network_ref['dhcp_server'] = self._get_dhcp_ip(context, network_ref) + self.driver.ensure_bridge(network_ref['bridge'], + network_ref['bridge_interface'], + network_ref) if not FLAGS.fake_network: - self.driver.update_dhcp(context, network['id']) - - def _on_set_network_host(self, context, network_id): - """Called when this host becomes the host for a project.""" - net = {} - net['dhcp_start'] = FLAGS.flat_network_dhcp_start - self.db.network_update(context, network_id, net) - network = db.network_get(context, network_id) - self.driver.ensure_bridge(network['bridge'], - network['bridge_interface'], - network) - if not FLAGS.fake_network: - self.driver.update_dhcp(context, network_id) + self.driver.update_dhcp(context, network_ref) if(FLAGS.use_ipv6): - self.driver.update_ra(context, network_id) + self.driver.update_ra(context, network_ref) + gateway = utils.get_my_linklocal(network_ref['bridge']) + self.db.network_update(context, network_ref['id'], + {'gateway_v6': gateway}) + + @property + def _create_bridge(self): + """Indicate whether this manager requires VIF to create a bridge.""" + return True class VlanManager(RPCAllocateFixedIP, FloatingIP, NetworkManager): @@ -809,8 +851,8 @@ class VlanManager(RPCAllocateFixedIP, FloatingIP, NetworkManager): values = {'allocated': True, 'virtual_interface_id': vif['id']} self.db.fixed_ip_update(context, address, values) - if not FLAGS.fake_network: - self.driver.update_dhcp(context, network['id']) + self._setup_network(context, network) + return address def add_network_to_project(self, context, project_id): """Force adds another network to a project.""" @@ -819,10 +861,7 @@ class VlanManager(RPCAllocateFixedIP, FloatingIP, NetworkManager): def _get_networks_for_instance(self, context, instance_id, project_id): """Determine which networks an instance should connect to.""" # get networks associated with project - networks = self.db.project_get_networks(context, project_id) - - # return only networks which have host set - return [network for network in networks if network['host']] + return self.db.project_get_networks(context, project_id) def create_networks(self, context, **kwargs): """Create networks based on parameters.""" @@ -841,33 +880,35 @@ class VlanManager(RPCAllocateFixedIP, FloatingIP, NetworkManager): NetworkManager.create_networks(self, context, vpn=True, **kwargs) - def _on_set_network_host(self, context, network_id): - """Called when this host becomes the host for a network.""" - network = self.db.network_get(context, network_id) - if not network['vpn_public_address']: + def _setup_network(self, context, network_ref): + """Sets up network on this host.""" + network_ref['dhcp_server'] = self._get_dhcp_ip(context, network_ref) + if not network_ref['vpn_public_address']: net = {} address = FLAGS.vpn_ip net['vpn_public_address'] = address - db.network_update(context, network_id, net) + network_ref = db.network_update(context, network_ref['id'], net) else: - address = network['vpn_public_address'] - - self.driver.ensure_vlan_bridge(network['vlan'], - network['bridge'], - network['bridge_interface'], - network) + address = network_ref['vpn_public_address'] + self.driver.ensure_vlan_bridge(network_ref['vlan'], + network_ref['bridge'], + network_ref['bridge_interface'], + network_ref) # NOTE(vish): only ensure this forward if the address hasn't been set # manually. if address == FLAGS.vpn_ip and hasattr(self.driver, "ensure_vlan_forward"): self.driver.ensure_vlan_forward(FLAGS.vpn_ip, - network['vpn_public_port'], - network['vpn_private_address']) + network_ref['vpn_public_port'], + network_ref['vpn_private_address']) if not FLAGS.fake_network: - self.driver.update_dhcp(context, network_id) + self.driver.update_dhcp(context, network_ref) if(FLAGS.use_ipv6): - self.driver.update_ra(context, network_id) + self.driver.update_ra(context, network_ref) + gateway = utils.get_my_linklocal(network_ref['bridge']) + self.db.network_update(context, network_ref['id'], + {'gateway_v6': gateway}) @property def _bottom_reserved_ips(self): @@ -879,3 +920,13 @@ class VlanManager(RPCAllocateFixedIP, FloatingIP, NetworkManager): """Number of reserved ips at the top of the range.""" parent_reserved = super(VlanManager, self)._top_reserved_ips return parent_reserved + FLAGS.cnt_vpn_clients + + @property + def _create_bridge(self): + """Indicate whether this manager requires VIF to create a bridge.""" + return True + + @property + def _create_vlan(self): + """Indicate whether this manager requires VIF to create a VLAN tag.""" + return True diff --git a/nova/tests/__init__.py b/nova/tests/__init__.py index e4ed75d37..7df6c06eb 100644 --- a/nova/tests/__init__.py +++ b/nova/tests/__init__.py @@ -59,6 +59,7 @@ def setup(): network.create_networks(ctxt, label='test', cidr=FLAGS.fixed_range, + multi_host=FLAGS.multi_host, num_networks=FLAGS.num_networks, network_size=FLAGS.network_size, cidr_v6=FLAGS.fixed_range_v6, @@ -68,7 +69,7 @@ def setup(): vpn_start=FLAGS.vpn_start, vlan_start=FLAGS.vlan_start) for net in db.network_get_all(ctxt): - network.set_network_host(ctxt, net['id']) + network.set_network_host(ctxt, net) cleandb = os.path.join(FLAGS.state_path, FLAGS.sqlite_clean_db) shutil.copyfile(testdb, cleandb) diff --git a/nova/tests/api/openstack/test_faults.py b/nova/tests/api/openstack/test_faults.py index 4d86ffb26..927009e77 100644 --- a/nova/tests/api/openstack/test_faults.py +++ b/nova/tests/api/openstack/test_faults.py @@ -139,3 +139,8 @@ class TestFaults(test.TestCase): self.assertEqual(resp.content_type, "application/xml") self.assertEqual(resp.status_int, 404) self.assertTrue('whut?' in resp.body) + + def test_fault_has_status_int(self): + """Ensure the status_int is set correctly on faults""" + fault = faults.Fault(webob.exc.HTTPBadRequest(explanation='what?')) + self.assertEqual(fault.status_int, 400) diff --git a/nova/tests/test_libvirt.py b/nova/tests/test_libvirt.py index 4878ed8d1..ad0931a89 100644 --- a/nova/tests/test_libvirt.py +++ b/nova/tests/test_libvirt.py @@ -62,6 +62,7 @@ def _create_network_info(count=1, ipv6=None): 'vlan': fake_vlan, 'bridge_interface': fake_bridge_interface} mapping = {'mac': fake, + 'dhcp_server': fake, 'gateway': fake, 'gateway6': fake, 'ips': [{'ip': fake_ip}, {'ip': fake_ip}]} @@ -721,6 +722,9 @@ class LibvirtConnTestCase(test.TestCase): return vdmock self.create_fake_libvirt_mock(lookupByName=fake_lookup) + self.mox.StubOutWithMock(self.compute, "recover_live_migration") + self.compute.recover_live_migration(self.context, instance_ref, + dest='dest') # Start test self.mox.ReplayAll() diff --git a/nova/tests/test_network.py b/nova/tests/test_network.py index 3ced1e5c4..57aa04ece 100644 --- a/nova/tests/test_network.py +++ b/nova/tests/test_network.py @@ -45,6 +45,7 @@ class FakeModel(dict): networks = [{'id': 0, 'label': 'test0', 'injected': False, + 'multi_host': False, 'cidr': '192.168.0.0/24', 'cidr_v6': '2001:db8::/64', 'gateway_v6': '2001:db8::1', @@ -62,6 +63,7 @@ networks = [{'id': 0, {'id': 1, 'label': 'test1', 'injected': False, + 'multi_host': False, 'cidr': '192.168.1.0/24', 'cidr_v6': '2001:db9::/64', 'gateway_v6': '2001:db9::1', @@ -122,20 +124,6 @@ class FlatNetworkTestCase(test.TestCase): self.network = network_manager.FlatManager(host=HOST) self.network.db = db - def test_set_network_hosts(self): - self.mox.StubOutWithMock(db, 'network_get_all') - self.mox.StubOutWithMock(db, 'network_set_host') - self.mox.StubOutWithMock(db, 'network_update') - - db.network_get_all(mox.IgnoreArg()).AndReturn([networks[0]]) - db.network_set_host(mox.IgnoreArg(), - networks[0]['id'], - mox.IgnoreArg()).AndReturn(HOST) - db.network_update(mox.IgnoreArg(), mox.IgnoreArg(), mox.IgnoreArg()) - self.mox.ReplayAll() - - self.network.set_network_hosts(None) - def test_get_instance_nw_info(self): self.mox.StubOutWithMock(db, 'fixed_ip_get_by_instance') self.mox.StubOutWithMock(db, 'virtual_interface_get_by_instance') @@ -149,7 +137,7 @@ class FlatNetworkTestCase(test.TestCase): mox.IgnoreArg()).AndReturn(flavor) self.mox.ReplayAll() - nw_info = self.network.get_instance_nw_info(None, 0, 0) + nw_info = self.network.get_instance_nw_info(None, 0, 0, None) self.assertTrue(nw_info) @@ -159,6 +147,7 @@ class FlatNetworkTestCase(test.TestCase): 'cidr': '192.168.%s.0/24' % i, 'cidr_v6': '2001:db%s::/64' % i8, 'id': i, + 'multi_host': False, 'injected': 'DONTCARE', 'bridge_interface': 'fake_fa%s' % i, 'vlan': None} @@ -166,6 +155,7 @@ class FlatNetworkTestCase(test.TestCase): self.assertDictMatch(nw[0], check) check = {'broadcast': '192.168.%s.255' % i, + 'dhcp_server': '192.168.%s.1' % i, 'dns': 'DONTCARE', 'gateway': '192.168.%s.1' % i, 'gateway6': '2001:db%s::1' % i8, @@ -173,7 +163,9 @@ class FlatNetworkTestCase(test.TestCase): 'ips': 'DONTCARE', 'label': 'test%s' % i, 'mac': 'DE:AD:BE:EF:00:0%s' % i, - 'rxtx_cap': 'DONTCARE'} + 'rxtx_cap': 'DONTCARE', + 'create_vlan': False, + 'create_bridge': False} self.assertDictMatch(nw[1], check) check = [{'enabled': 'DONTCARE', diff --git a/nova/tests/test_xenapi.py b/nova/tests/test_xenapi.py index 4cb7447d3..199a8bc52 100644 --- a/nova/tests/test_xenapi.py +++ b/nova/tests/test_xenapi.py @@ -647,7 +647,7 @@ class XenAPIVMTestCase(test.TestCase): self.flags(xenapi_inject_image=False) instance = self._create_instance() conn = xenapi_conn.get_connection(False) - conn.rescue(instance, None) + conn.rescue(instance, None, []) def test_unrescue(self): instance = self._create_instance() diff --git a/nova/virt/driver.py b/nova/virt/driver.py index e8dcf138b..2faff0a1e 100644 --- a/nova/virt/driver.py +++ b/nova/virt/driver.py @@ -257,4 +257,3 @@ class ComputeDriver(object): def plug_vifs(self, instance, network_info): """Plugs in VIFs to networks.""" raise NotImplementedError() - diff --git a/nova/virt/libvirt/connection.py b/nova/virt/libvirt/connection.py index 7b2ff49b2..585cd74a3 100644 --- a/nova/virt/libvirt/connection.py +++ b/nova/virt/libvirt/connection.py @@ -126,7 +126,7 @@ flags.DEFINE_bool('start_guests_on_host_boot', False, flags.DEFINE_string('libvirt_vif_type', 'bridge', 'Type of VIF to create.') flags.DEFINE_string('libvirt_vif_driver', - 'nova.virt.libvirt.vif.LibvirtVlanBridgeDriver', + 'nova.virt.libvirt.vif.LibvirtBridgeDriver', 'The libvirt VIF driver to configure the VIFs.') diff --git a/nova/virt/libvirt/vif.py b/nova/virt/libvirt/vif.py index 4e3dcddff..bf3579608 100644 --- a/nova/virt/libvirt/vif.py +++ b/nova/virt/libvirt/vif.py @@ -19,26 +19,26 @@ """VIF drivers for libvirt.""" from nova import flags +from nova import log as logging from nova.network import linux_net from nova.virt.libvirt import netutils from nova import utils from nova.virt.vif import VIFDriver +LOG = logging.getLogger('nova.virt.libvirt.vif') + FLAGS = flags.FLAGS -flags.DEFINE_bool('allow_project_net_traffic', - True, - 'Whether to allow in project network traffic') flags.DEFINE_string('libvirt_ovs_integration_bridge', 'br-int', - 'Name of Integration Bridge used by Open vSwitch') + 'Name of Integration Bridge used by Open vSwitch') + -class LibvirtBridge(object): - """Linux bridge VIF for Libvirt.""" +class LibvirtBridgeDriver(VIFDriver): + """VIF driver for Linux bridge.""" - def get_configurations(self, network, mapping): + def _get_configurations(self, network, mapping): """Get a dictionary of VIF configurations for bridge type.""" # Assume that the gateway also acts as the dhcp server. - dhcp_server = mapping['gateway'] gateway6 = mapping.get('gateway6') mac_id = mapping['mac'].replace(':', '') @@ -61,7 +61,7 @@ class LibvirtBridge(object): 'bridge_name': network['bridge'], 'mac_address': mapping['mac'], 'ip_address': mapping['ips'][0]['ip'], - 'dhcp_server': dhcp_server, + 'dhcp_server': mapping['dhcp_server'], 'extra_params': extra_params, } @@ -70,30 +70,25 @@ class LibvirtBridge(object): return result - -class LibvirtBridgeDriver(VIFDriver, LibvirtBridge): - """VIF driver for Linux bridge.""" - def plug(self, instance, network, mapping): """Ensure that the bridge exists, and add VIF to it.""" - linux_net.ensure_bridge(network['bridge'], - network['bridge_interface']) - return self.get_configurations(network, mapping) - - def unplug(self, instance, network, mapping): - pass - - -class LibvirtVlanBridgeDriver(VIFDriver, LibvirtBridge): - """VIF driver for Linux bridge with VLAN.""" - - def plug(self, instance, network, mapping): - """Ensure that VLAN and bridge exist and add VIF to the bridge.""" - linux_net.ensure_vlan_bridge(network['vlan'], network['bridge'], - network['bridge_interface']) - return self.get_configurations(network, mapping) + if not network.get('multi_host') and mapping.get('create_bridge'): + if mapping.get('create_vlan'): + LOG.debug(_('Ensuring vlan %(vlan)s and bridge %(bridge)s'), + {'vlan': network['vlan'], + 'bridge': network['bridge']}) + linux_net.ensure_vlan_bridge(network['vlan'], + network['bridge'], + network['bridge_interface']) + else: + LOG.debug(_("Ensuring bridge %s"), network['bridge']) + linux_net.ensure_bridge(network['bridge'], + network['bridge_interface']) + + return self._get_configurations(network, mapping) def unplug(self, instance, network, mapping): + """No manual unplugging required.""" pass diff --git a/nova/virt/vmwareapi/vif.py b/nova/virt/vmwareapi/vif.py index 902699089..281565bbc 100644 --- a/nova/virt/vmwareapi/vif.py +++ b/nova/virt/vmwareapi/vif.py @@ -53,7 +53,8 @@ class VMWareVlanBridgeDriver(VIFDriver): session = VMWareAPISession(host_ip, host_username, host_password, FLAGS.vmwareapi_api_retry_count) vlan_interface = bridge_interface - # Check if the vlan_interface physical network adapter exists on the host + # Check if the vlan_interface physical network adapter exists on the + # host. if not network_utils.check_if_vlan_interface_exists(session, vlan_interface): raise exception.NetworkAdapterNotFound(adapter=vlan_interface) @@ -62,32 +63,33 @@ class VMWareVlanBridgeDriver(VIFDriver): vswitch_associated = network_utils.get_vswitch_for_vlan_interface( session, vlan_interface) if vswitch_associated is None: - raise exception.SwicthNotFoundForNetworkAdapter(adapter=vlan_interface) + raise exception.SwicthNotFoundForNetworkAdapter( + adapter=vlan_interface) # Check whether bridge already exists and retrieve the the ref of the # network whose name_label is "bridge" network_ref = network_utils.get_network_with_the_name(session, bridge) if network_ref is None: - # Create a port group on the vSwitch associated with the vlan_interface - # corresponding physical network adapter on the ESX host - network_utils.create_port_group(session, bridge, vswitch_associated, - vlan_num) + # Create a port group on the vSwitch associated with the + # vlan_interface corresponding physical network adapter on the ESX + # host. + network_utils.create_port_group(session, bridge, + vswitch_associated, vlan_num) else: # Get the vlan id and vswitch corresponding to the port group pg_vlanid, pg_vswitch = \ - network_utils.get_vlanid_and_vswitch_for_portgroup(session, bridge) + network_utils.get_vlanid_and_vswitch_for_portgroup(session, + bridge) # Check if the vswitch associated is proper if pg_vswitch != vswitch_associated: - raise exception.InvalidVLANPortGroup(bridge=bridge, - expected=vswitch_associated, - actual=pg_vswitch) + raise exception.InvalidVLANPortGroup( + bridge=bridge, expected=vswitch_associated, + actual=pg_vswitch) # Check if the vlan id is proper for the port group if pg_vlanid != vlan_num: raise exception.InvalidVLANTag(bridge=bridge, tag=vlan_num, pgroup=pg_vlanid) - def unplug(self, instance, network, mapping): pass - diff --git a/nova/virt/vmwareapi/vmops.py b/nova/virt/vmwareapi/vmops.py index eded4391e..6cc130809 100644 --- a/nova/virt/vmwareapi/vmops.py +++ b/nova/virt/vmwareapi/vmops.py @@ -806,4 +806,3 @@ class VMWareVMOps(object): """Unplug VIFs from networks."""
for (network, mapping) in network_info:
self._vif_driver.unplug(instance, network, mapping)
-
diff --git a/nova/virt/vmwareapi_conn.py b/nova/virt/vmwareapi_conn.py index 2492c9951..ce57847b2 100644 --- a/nova/virt/vmwareapi_conn.py +++ b/nova/virt/vmwareapi_conn.py @@ -198,6 +198,7 @@ class VMWareESXConnection(driver.ComputeDriver): """Plugs in VIFs to networks."""
self._vmops.plug_vifs(instance, network_info)
+
class VMWareAPISession(object):
"""
Sets up a session with the ESX host and handles all
diff --git a/nova/virt/xenapi/vif.py b/nova/virt/xenapi/vif.py index 3604ef8a9..beee2390d 100644 --- a/nova/virt/xenapi/vif.py +++ b/nova/virt/xenapi/vif.py @@ -26,17 +26,18 @@ from nova.virt.xenapi.network_utils import NetworkHelper FLAGS = flags.FLAGS flags.DEFINE_string('xenapi_ovs_integration_bridge', 'xapi1', - 'Name of Integration Bridge used by Open vSwitch') + 'Name of Integration Bridge used by Open vSwitch') LOG = logging.getLogger("nova.virt.xenapi.vif") + class XenAPIBridgeDriver(VIFDriver): """Combined VIF and Bridge class for XenAPI.""" def get_vif_rec(self, xenapi_session, vm_ref, instance, device, network, network_mapping): - network_ref = NetworkHelper.find_network_with_bridge(xenapi_session, - network['bridge']) + network_ref = NetworkHelper.find_network_with_bridge(xenapi_session, + network['bridge']) rxtx_cap = network_mapping.pop('rxtx_cap') vif_rec = {} vif_rec['device'] = str(device) @@ -55,66 +56,68 @@ class XenAPIBridgeDriver(VIFDriver): vlan_num = network['vlan'] if vlan_num is None: - return # no plugging done for non-VLAN networks - bridge = network['bridge'] + return # no plugging done for non-VLAN networks + bridge = network['bridge'] bridge_interface = network['bridge_interface'] - # Check whether bridge already exists - # Retrieve network whose name_label is "bridge" - network_ref = network_utils.NetworkHelper.find_network_with_name_label( - xenapi_session, - bridge) + # Check whether bridge already exists + # Retrieve network whose name_label is "bridge" + network_ref = network_utils.NetworkHelper.find_network_with_name_label( + xenapi_session, bridge) + if network_ref is None: - # If bridge does not exists - # 1 - create network - description = 'network for nova bridge %s' % bridge - network_rec = {'name_label': bridge, - 'name_description': description, - 'other_config': {}} - network_ref = xenapi_session.call_xenapi('network.create', network_rec) - # 2 - find PIF for VLAN NOTE(salvatore-orlando): using double - # quotes inside single quotes as xapi filter only support - # tokens in double quotes - expr = 'field "device" = "%s" and \ + # If bridge does not exists + # 1 - create network + description = 'network for nova bridge %s' % bridge + network_rec = {'name_label': bridge, + 'name_description': description, + 'other_config': {}} + network_ref = xenapi_session.call_xenapi('network.create', + network_rec) + # 2 - find PIF for VLAN NOTE(salvatore-orlando): using double + # quotes inside single quotes as xapi filter only support + # tokens in double quotes + expr = 'field "device" = "%s" and \ field "VLAN" = "-1"' % bridge_interface - pifs = xenapi_session.call_xenapi('PIF.get_all_records_where', expr) - pif_ref = None - # Multiple PIF are ok: we are dealing with a pool - if len(pifs) == 0: - raise Exception( - _('Found no PIF for device %s') % bridge_interface) + pifs = xenapi_session.call_xenapi('PIF.get_all_records_where', + expr) + pif_ref = None + # Multiple PIF are ok: we are dealing with a pool + if len(pifs) == 0: + raise Exception(_('Found no PIF for device %s') % + bridge_interface) + # 3 - create vlan for network for pif_ref in pifs.keys(): - xenapi_session.call_xenapi('VLAN.create', - pif_ref, - str(vlan_num), - network_ref) - else: - # Check VLAN tag is appropriate - network_rec = xenapi_session.call_xenapi('network.get_record', - network_ref) - # Retrieve PIFs from network - for pif_ref in network_rec['PIFs']: - # Retrieve VLAN from PIF - pif_rec = xenapi_session.call_xenapi('PIF.get_record', pif_ref) - pif_vlan = int(pif_rec['VLAN']) - # Raise an exception if VLAN != vlan_num - if pif_vlan != vlan_num: - raise Exception(_("PIF %(pif_rec['uuid'])s for network " - "%(bridge)s has VLAN id %(pif_vlan)d. " - "Expected %(vlan_num)d") % locals()) + xenapi_session.call_xenapi('VLAN.create', pif_ref, + str(vlan_num), network_ref) + else: + # Check VLAN tag is appropriate + network_rec = xenapi_session.call_xenapi('network.get_record', + network_ref) + # Retrieve PIFs from network + for pif_ref in network_rec['PIFs']: + # Retrieve VLAN from PIF + pif_rec = xenapi_session.call_xenapi('PIF.get_record', pif_ref) + pif_vlan = int(pif_rec['VLAN']) + # Raise an exception if VLAN != vlan_num + if pif_vlan != vlan_num: + raise Exception(_("PIF %(pif_rec['uuid'])s for network " + "%(bridge)s has VLAN id %(pif_vlan)d. " + "Expected %(vlan_num)d") % locals()) def unplug(self, instance, network, mapping): pass + class XenAPIOpenVswitchDriver(VIFDriver): """VIF driver for Open vSwitch with XenAPI.""" def get_vif_rec(self, xenapi_session, vm_ref, instance, device, network, network_mapping): - # with OVS model, always plug into OVS integration bridge - network_ref = NetworkHelper.find_network_with_bridge(xenapi_session, - FLAGS.xenapi_ovs_integration_bridge) + # with OVS model, always plug into OVS integration bridge + network_ref = NetworkHelper.find_network_with_bridge( + xenapi_session, FLAGS.xenapi_ovs_integration_bridge) vif_rec = {} vif_rec['device'] = str(device) vif_rec['network'] = network_ref @@ -126,14 +129,13 @@ class XenAPIOpenVswitchDriver(VIFDriver): vif_rec['qos_algorithm_params'] = {} # OVS on the hypervisor monitors this key and uses it to # set the iface-id attribute - vif_rec['other_config'] = {"nicira-iface-id" : vif_id } + vif_rec['other_config'] = {"nicira-iface-id": vif_id} return vif_rec def plug(self, xenapi_session, instance, network, mapping): - # nothing to do, as this driver assumes that integration bridge - # was created by administrator + # nothing to do, as this driver assumes that integration bridge + # was created by administrator pass def unplug(self, instance, network, mapping): - pass - + pass diff --git a/nova/virt/xenapi/vm_utils.py b/nova/virt/xenapi/vm_utils.py index e63582b54..62863c6d8 100644 --- a/nova/virt/xenapi/vm_utils.py +++ b/nova/virt/xenapi/vm_utils.py @@ -282,7 +282,6 @@ class VMHelper(HelperBase): LOG.exception(exc) raise StorageError(_('Unable to destroy VDI %s') % vdi_ref) - @classmethod def create_vdi(cls, session, sr_ref, name_label, virtual_size, read_only): """Create a VDI record and returns its reference.""" @@ -1095,6 +1094,7 @@ def _stream_disk(dev, image_type, virtual_size, image_file): for chunk in image_file: f.write(chunk) + def _write_partition(virtual_size, dev): dest = '/dev/%s' % dev primary_first = MBR_SIZE_SECTORS diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py index bdf471d16..4fa59b6b7 100644 --- a/nova/virt/xenapi/vmops.py +++ b/nova/virt/xenapi/vmops.py @@ -344,6 +344,7 @@ class VMOps(object): _check_agent_version() _inject_files() _set_admin_password() + self.reset_network(instance, vm_ref) return True except Exception, exc: LOG.warn(exc) @@ -353,9 +354,6 @@ class VMOps(object): timer.f = _wait_for_boot - # call to reset network to configure network from xenstore - self.reset_network(instance, vm_ref) - return timer.start(interval=0.5, now=True) def _handle_spawn_error(self, vdis, spawn_error): @@ -886,7 +884,6 @@ class VMOps(object): instance['name']) raise - def _wait_with_callback(self, instance_id, task, callback): ret = None try: diff --git a/plugins/xenserver/xenapi/etc/xapi.d/plugins/agent b/plugins/xenserver/xenapi/etc/xapi.d/plugins/agent index 288ccc78a..d609a88ab 100755 --- a/plugins/xenserver/xenapi/etc/xapi.d/plugins/agent +++ b/plugins/xenserver/xenapi/etc/xapi.d/plugins/agent @@ -72,7 +72,9 @@ def key_init(self, arg_dict): info to be passed, such as passwords. Returns the shared secret key value. """ - pub = int(arg_dict["pub"]) + # WARNING: Some older Windows agents will crash if the public key isn't + # a string + pub = arg_dict["pub"] arg_dict["value"] = json.dumps({"name": "keyinit", "value": pub}) request_id = arg_dict["id"] arg_dict["path"] = "data/host/%s" % request_id |
