From 005a4e645f8e913c673c6ba07e7b0c8c54f33e1c Mon Sep 17 00:00:00 2001 From: Ed Leafe Date: Tue, 21 Dec 2010 14:17:29 -0600 Subject: Refactored duplicate rpc.cast() calls in nova/compute/api.py. Cleaned up some formatting issues. --- nova/compute/api.py | 47 ++++++++++++--------------------- nova/compute/manager.py | 32 +++++++++++++++++++++++ nova/tests/compute_unittest.py | 7 +++++ nova/virt/fake.py | 12 +++++++++ nova/virt/xenapi/vmops.py | 15 ++++++++--- nova/virt/xenapi_conn.py | 59 ++++++++++++++++++++++-------------------- 6 files changed, 111 insertions(+), 61 deletions(-) (limited to 'nova') diff --git a/nova/compute/api.py b/nova/compute/api.py index c740814da..ae7c84bc5 100644 --- a/nova/compute/api.py +++ b/nova/compute/api.py @@ -206,8 +206,7 @@ class ComputeAPI(base.Base): def delete_instance(self, context, instance_id): logging.debug("Going to try and terminate %d" % instance_id) try: - instance = self.db.instance_get_by_internal_id(context, - instance_id) + instance = self.get_instance(context, instance_id) except exception.NotFound as e: logging.warning("Instance %d was not found during terminate", instance_id) @@ -271,50 +270,38 @@ class ComputeAPI(base.Base): def get_instance(self, context, instance_id): return self.db.instance_get_by_internal_id(context, instance_id) - def reboot(self, context, instance_id): - """Reboot the given instance.""" - instance = self.db.instance_get_by_internal_id(context, instance_id) + def _cast_compute_message(method, context, instance_id): + """Generic handler for RPC calls.""" + instance = self.get_instance(context, instance_id) host = instance['host'] rpc.cast(context, self.db.queue_get_for(context, FLAGS.compute_topic, host), - {"method": "reboot_instance", + {"method": method, "args": {"instance_id": instance['id']}}) + def reboot(self, context, instance_id): + """Reboot the given instance.""" + self._cast_compute_message("reboot_instance", context, instance_id) + def pause(self, context, instance_id): """Pause the given instance.""" - instance = self.db.instance_get_by_internal_id(context, instance_id) - host = instance['host'] - rpc.cast(context, - self.db.queue_get_for(context, FLAGS.compute_topic, host), - {"method": "pause_instance", - "args": {"instance_id": instance['id']}}) + self._cast_compute_message("pause_instance", context, instance_id) def unpause(self, context, instance_id): """Unpause the given instance.""" - instance = self.db.instance_get_by_internal_id(context, instance_id) - host = instance['host'] - rpc.cast(context, - self.db.queue_get_for(context, FLAGS.compute_topic, host), - {"method": "unpause_instance", - "args": {"instance_id": instance['id']}}) + self._cast_compute_message("unpause_instance", context, instance_id) def rescue(self, context, instance_id): """Rescue the given instance.""" - instance = self.db.instance_get_by_internal_id(context, instance_id) - host = instance['host'] - rpc.cast(context, - self.db.queue_get_for(context, FLAGS.compute_topic, host), - {"method": "rescue_instance", - "args": {"instance_id": instance['id']}}) + self._cast_compute_message("rescue_instance", context, instance_id) def unrescue(self, context, instance_id): """Unrescue the given instance.""" - instance = self.db.instance_get_by_internal_id(context, instance_id) - host = instance['host'] - rpc.cast(context, - self.db.queue_get_for(context, FLAGS.compute_topic, host), - {"method": "unrescue_instance", - "args": {"instance_id": instance['id']}}) + self._cast_compute_message("unrescue_instance", context, instance_id) + + def reset_root_password(self, context, instance_id): + """Reset the root/admin pw for the given instance.""" + self._cast_compute_message("reset_root_password", context, instance_id) def _get_network_topic(self, context): """Retrieves the network host for a project""" diff --git a/nova/compute/manager.py b/nova/compute/manager.py index a84af6bb9..cb64cd39d 100644 --- a/nova/compute/manager.py +++ b/nova/compute/manager.py @@ -156,6 +156,38 @@ class ComputeManager(manager.Manager): self.driver.reboot(instance_ref) self._update_state(context, instance_id) + + + + # WORKING CODE + @exception.wrap_exception + def reset_root_password(self, context, instance_id): + """Reset the root/admin password for an instance on this server.""" + context = context.elevated() + instance_ref = self.db.instance_get(context, instance_id) + self._update_state(context, instance_id) + + if instance_ref['state'] != power_state.RUNNING: + logging.warn('trying to reset the password on a non-running ' + 'instance: %s (state: %s excepted: %s)', + instance_ref['internal_id'], + instance_ref['state'], + power_state.RUNNING) + + logging.debug('instance %s: resetting root password', + instance_ref['name']) + self.db.instance_set_state(context, + instance_id, + power_state.NOSTATE, + 'resetting_password') + #TODO: (dabo) not sure how we will implement this yet. + self.driver.reset_root_password(instance_ref) + self._update_state(context, instance_id) + + + + + @exception.wrap_exception def rescue_instance(self, context, instance_id): """Rescue an instance on this server.""" diff --git a/nova/tests/compute_unittest.py b/nova/tests/compute_unittest.py index 187ca31de..16e577c56 100644 --- a/nova/tests/compute_unittest.py +++ b/nova/tests/compute_unittest.py @@ -142,6 +142,13 @@ class ComputeTestCase(test.TestCase): self.compute.reboot_instance(self.context, instance_id) self.compute.terminate_instance(self.context, instance_id) + def test_reset_root_password(self): + """Ensure instance can have its root password reset""" + instance_id = self._create_instance() + self.compute.run_instance(self.context, instance_id) + self.compute.reset_root_password(self.context, instance_id) + self.compute.terminate_instance(self.context, instance_id) + def test_console_output(self): """Make sure we can get console output from instance""" instance_id = self._create_instance() diff --git a/nova/virt/fake.py b/nova/virt/fake.py index 55c6dcef9..ff3f61838 100644 --- a/nova/virt/fake.py +++ b/nova/virt/fake.py @@ -118,6 +118,18 @@ class FakeConnection(object): """ pass + def reset_root_password(self, instance): + """ + Reset the root password on the specified instance. + + The given parameter is an instance of nova.compute.service.Instance, + and so the instance is being specified as instance.name. + + The work will be done asynchronously. This function returns a + Deferred that allows the caller to detect when it is complete. + """ + pass + def rescue(self, instance): """ Rescue the specified instance. diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py index a18eacf07..c5ae52add 100644 --- a/nova/virt/xenapi/vmops.py +++ b/nova/virt/xenapi/vmops.py @@ -34,7 +34,6 @@ class VMOps(object): """ Management class for VM-related tasks """ - def __init__(self, session): global XenAPI if XenAPI is None: @@ -45,8 +44,9 @@ class VMOps(object): def list_instances(self): """ List VM instances """ - return [self._session.get_xenapi().VM.get_name_label(vm) \ - for vm in self._session.get_xenapi().VM.get_all()] + xVM = self._session.get_xenapi().VM + return [xVM.get_name_label(vm) + for vm in xVM.get_all()] def spawn(self, instance): """ Create VM instance """ @@ -89,6 +89,15 @@ class VMOps(object): task = self._session.call_xenapi('Async.VM.clean_reboot', vm) self._session.wait_for_task(task) + def reset_root_password(self, instance): + """ Reset the root/admin password on the VM instance """ + instance_name = instance.name + vm = VMHelper.lookup(self._session, instance_name) + if vm is None: + raise Exception('instance not present %s' % instance_name) + task = self._session.call_xenapi('Async.VM.reset_root_password', vm) + self._session.wait_for_task(task) + def destroy(self, instance): """ Destroy VM instance """ vm = VMHelper.lookup(self._session, instance.name) diff --git a/nova/virt/xenapi_conn.py b/nova/virt/xenapi_conn.py index 21ed2cd65..199c0b862 100644 --- a/nova/virt/xenapi_conn.py +++ b/nova/virt/xenapi_conn.py @@ -19,15 +19,15 @@ A connection to XenServer or Xen Cloud Platform. The concurrency model for this class is as follows: -All XenAPI calls are on a thread (using t.i.t.deferToThread, via the decorator -deferredToThread). They are remote calls, and so may hang for the usual -reasons. They should not be allowed to block the reactor thread. +All XenAPI calls are on a green thread (using eventlet's "tpool" +thread pool). They are remote calls, and so may hang for the usual +reasons. All long-running XenAPI calls (VM.start, VM.reboot, etc) are called async -(using XenAPI.VM.async_start etc). These return a task, which can then be -polled for completion. Polling is handled using reactor.callLater. +(using XenAPI.VM.async_start etc). These return a task, which can then be +polled for completion. -This combination of techniques means that we don't block the reactor thread at +This combination of techniques means that we don't block the main thread at all, and at the same time we don't hold lots of threads waiting for long-running operations. @@ -75,7 +75,7 @@ flags.DEFINE_string('xenapi_connection_password', flags.DEFINE_float('xenapi_task_poll_interval', 0.5, 'The interval used for polling of remote tasks ' - '(Async.VM.start, etc). Used only if ' + '(Async.VM.start, etc). Used only if ' 'connection_type=xenapi.') XenAPI = None @@ -101,7 +101,7 @@ def get_connection(_): class XenAPIConnection(object): - """ A connection to XenServer or Xen Cloud Platform """ + """A connection to XenServer or Xen Cloud Platform""" def __init__(self, url, user, pw): session = XenAPISession(url, user, pw) @@ -109,31 +109,35 @@ class XenAPIConnection(object): self._volumeops = VolumeOps(session) def list_instances(self): - """ List VM instances """ + """List VM instances""" return self._vmops.list_instances() def spawn(self, instance): - """ Create VM instance """ + """Create VM instance""" self._vmops.spawn(instance) def reboot(self, instance): - """ Reboot VM instance """ + """Reboot VM instance""" self._vmops.reboot(instance) + def reset_root_password(self, instance): + """Reset the root/admin password on the VM instance""" + self._vmops.reset_root_password(instance) + def destroy(self, instance): - """ Destroy VM instance """ + """Destroy VM instance""" self._vmops.destroy(instance) def pause(self, instance, callback): - """ Pause VM instance """ + """Pause VM instance""" self._vmops.pause(instance, callback) def unpause(self, instance, callback): - """ Unpause paused VM instance """ + """Unpause paused VM instance""" self._vmops.unpause(instance, callback) def get_info(self, instance_id): - """ Return data about VM instance """ + """Return data about VM instance""" return self._vmops.get_info(instance_id) def get_diagnostics(self, instance_id): @@ -141,33 +145,33 @@ class XenAPIConnection(object): return self._vmops.get_diagnostics(instance_id) def get_console_output(self, instance): - """ Return snapshot of console """ + """Return snapshot of console""" return self._vmops.get_console_output(instance) def attach_volume(self, instance_name, device_path, mountpoint): - """ Attach volume storage to VM instance """ + """Attach volume storage to VM instance""" return self._volumeops.attach_volume(instance_name, - device_path, - mountpoint) + device_path, + mountpoint) def detach_volume(self, instance_name, mountpoint): - """ Detach volume storage to VM instance """ + """Detach volume storage to VM instance""" return self._volumeops.detach_volume(instance_name, mountpoint) class XenAPISession(object): - """ The session to invoke XenAPI SDK calls """ + """The session to invoke XenAPI SDK calls""" def __init__(self, url, user, pw): self._session = XenAPI.Session(url) self._session.login_with_password(user, pw) def get_xenapi(self): - """ Return the xenapi object """ + """Return the xenapi object""" return self._session.xenapi def get_xenapi_host(self): - """ Return the xenapi host """ + """Return the xenapi host""" return self._session.xenapi.session.get_this_host(self._session.handle) def call_xenapi(self, method, *args): @@ -184,9 +188,8 @@ class XenAPISession(object): self.get_xenapi_host(), plugin, fn, args) def wait_for_task(self, task): - """Return a Deferred that will give the result of the given task. + """Return the result of the given task. The task is polled until it completes.""" - done = event.Event() loop = utils.LoopingCall(self._poll_task, task, done) loop.start(FLAGS.xenapi_task_poll_interval, now=True) @@ -195,7 +198,7 @@ class XenAPISession(object): return rv def _poll_task(self, task, done): - """Poll the given XenAPI task, and fire the given Deferred if we + """Poll the given XenAPI task, and fire the given action if we get a result.""" try: #logging.debug('Polling task %s...', task) @@ -218,7 +221,7 @@ class XenAPISession(object): def _unwrap_plugin_exceptions(func, *args, **kwargs): - """ Parse exception details """ + """Parse exception details""" try: return func(*args, **kwargs) except XenAPI.Failure, exc: @@ -240,7 +243,7 @@ def _unwrap_plugin_exceptions(func, *args, **kwargs): def _parse_xmlrpc_value(val): - """Parse the given value as if it were an XML-RPC value. This is + """Parse the given value as if it were an XML-RPC value. This is sometimes used as the format for the task.result field.""" if not val: return val -- cgit From 269ab03f74ea94a586f6af5b7d61847443522ba1 Mon Sep 17 00:00:00 2001 From: Ed Leafe Date: Wed, 22 Dec 2010 11:20:30 -0600 Subject: committing so that I can merge trunk changes --- nova/compute/manager.py | 10 +--------- nova/virt/xenapi/vmops.py | 22 ++++++++++++---------- 2 files changed, 13 insertions(+), 19 deletions(-) (limited to 'nova') diff --git a/nova/compute/manager.py b/nova/compute/manager.py index cb64cd39d..86f8d9216 100644 --- a/nova/compute/manager.py +++ b/nova/compute/manager.py @@ -156,10 +156,6 @@ class ComputeManager(manager.Manager): self.driver.reboot(instance_ref) self._update_state(context, instance_id) - - - - # WORKING CODE @exception.wrap_exception def reset_root_password(self, context, instance_id): """Reset the root/admin password for an instance on this server.""" @@ -180,14 +176,10 @@ class ComputeManager(manager.Manager): instance_id, power_state.NOSTATE, 'resetting_password') - #TODO: (dabo) not sure how we will implement this yet. + #### TODO: (dabo) not sure how we will implement this yet. self.driver.reset_root_password(instance_ref) self._update_state(context, instance_id) - - - - @exception.wrap_exception def rescue_instance(self, context, instance_id): """Rescue an instance on this server.""" diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py index c5ae52add..9dca55e26 100644 --- a/nova/virt/xenapi/vmops.py +++ b/nova/virt/xenapi/vmops.py @@ -43,13 +43,13 @@ class VMOps(object): VMHelper.late_import() def list_instances(self): - """ List VM instances """ + """List VM instances""" xVM = self._session.get_xenapi().VM return [xVM.get_name_label(vm) for vm in xVM.get_all()] def spawn(self, instance): - """ Create VM instance """ + """Create VM instance""" vm = VMHelper.lookup(self._session, instance.name) if vm is not None: raise Exception('Attempted to create non-unique name %s' % @@ -81,7 +81,7 @@ class VMOps(object): vm_ref) def reboot(self, instance): - """ Reboot VM instance """ + """Reboot VM instance""" instance_name = instance.name vm = VMHelper.lookup(self._session, instance_name) if vm is None: @@ -90,16 +90,18 @@ class VMOps(object): self._session.wait_for_task(task) def reset_root_password(self, instance): - """ Reset the root/admin password on the VM instance """ + """Reset the root/admin password on the VM instance""" instance_name = instance.name vm = VMHelper.lookup(self._session, instance_name) if vm is None: raise Exception('instance not present %s' % instance_name) - task = self._session.call_xenapi('Async.VM.reset_root_password', vm) + #### TODO: (dabo) Need to figure out the correct command to + #### write to the xenstore. + task = self._session.call_xenapi('VM.get xenstore data', vm) self._session.wait_for_task(task) def destroy(self, instance): - """ Destroy VM instance """ + """Destroy VM instance""" vm = VMHelper.lookup(self._session, instance.name) if vm is None: # Don't complain, just return. This lets us clean up instances @@ -136,7 +138,7 @@ class VMOps(object): callback(ret) def pause(self, instance, callback): - """ Pause VM instance """ + """Pause VM instance""" instance_name = instance.name vm = VMHelper.lookup(self._session, instance_name) if vm is None: @@ -145,7 +147,7 @@ class VMOps(object): self._wait_with_callback(task, callback) def unpause(self, instance, callback): - """ Unpause VM instance """ + """Unpause VM instance""" instance_name = instance.name vm = VMHelper.lookup(self._session, instance_name) if vm is None: @@ -154,7 +156,7 @@ class VMOps(object): self._wait_with_callback(task, callback) def get_info(self, instance_id): - """ Return data about VM instance """ + """Return data about VM instance""" vm = VMHelper.lookup_blocking(self._session, instance_id) if vm is None: raise Exception('instance not present %s' % instance_id) @@ -170,6 +172,6 @@ class VMOps(object): return VMHelper.compile_diagnostics(self._session, rec) def get_console_output(self, instance): - """ Return snapshot of console """ + """Return snapshot of console""" # TODO: implement this to fix pylint! return 'FAKE CONSOLE OUTPUT of instance' -- cgit From 6c8fe1963e6d64ba76698dbbaeb7ef9f63cfda95 Mon Sep 17 00:00:00 2001 From: Ed Leafe Date: Wed, 22 Dec 2010 17:33:21 -0600 Subject: Got basic xenstore operations working --- nova/virt/xenapi/vmops.py | 63 +++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 55 insertions(+), 8 deletions(-) (limited to 'nova') diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py index 1c76d2ccc..e36770cfc 100644 --- a/nova/virt/xenapi/vmops.py +++ b/nova/virt/xenapi/vmops.py @@ -92,16 +92,63 @@ class VMOps(object): task = self._session.call_xenapi('Async.VM.clean_reboot', vm) self._session.wait_for_task(instance.id, task) - def reset_root_password(self, instance): - """Reset the root/admin password on the VM instance""" - instance_name = instance.name - vm = VMHelper.lookup(self._session, instance_name) + def _get_vm_opaque_ref(self, instance_or_vm): + try: + instance_name = instance_or_vm.name + vm = VMHelper.lookup(self._session, instance_name) + except AttributeError: + # A vm opaque ref was passed + vm = instance_or_vm if vm is None: raise Exception('instance not present %s' % instance_name) - #### TODO: (dabo) Need to figure out the correct command to - #### write to the xenstore. - task = self._session.call_xenapi('VM.get_xenstore_data', vm) - self._session.wait_for_task(task) + return vm + + def remove_from_xenstore(self, instance_or_vm, keys): + vm = self._get_vm_opaque_ref(instance_or_vm) + for key in keys: + self._session._session.xenapi_request('VM.remove_from_xenstore_data', + (vm, key)) + + def read_from_xenstore(self, instance_or_vm, keys=None): + """Returns the xenstore data for the specified VM instance as + a dict. Accepts an optional list of keys; if the list of keys is + passed, the returned dict is filtered to only return the values + for those keys. + """ + vm = self._get_vm_opaque_ref(instance_or_vm) + ret = self._session._session.xenapi_request('VM.get_xenstore_data', (vm, )) + if keys: + allkeys = set(ret.keys()) + badkeys = allkeys.difference(keys) + for k in badkeys: + ret.pop(k) + return ret + + def add_to_xenstore(self, instance_or_vm, mapping): + """Takes a dict and adds it to the xenstore record for + the given vm instance. Existing data is preserved, but any + existing values for the mapping's keys are overwritten. + """ + vm = self._get_vm_opaque_ref(instance_or_vm) + current_data = self.read_from_xenstore(vm) + current_data.update(mapping) + self.write_to_xenstore(vm, current_data) + + def write_to_xenstore(self, instance_or_vm, mapping): + """Takes a dict and writes it to the xenstore record for + the given vm instance. Any existing data is overwritten. + """ + vm = self._get_vm_opaque_ref(instance_or_vm) + self._session._session.xenapi_request('VM.set_xenstore_data', + (vm, mapping)) + + def reset_root_password(self, instance): + """Reset the root/admin password on the VM instance""" + self.add_to_xenstore(instance, {"reset_root_password": "requested"}) + self.add_to_xenstore(instance, {"TEST": "OMG!"}) + import time + self.add_to_xenstore(instance, {"timestamp": time.ctime()}) + def destroy(self, instance): """Destroy VM instance""" -- cgit From ef8e4495f5ed195a08be6c02b3eb3326f6403bb6 Mon Sep 17 00:00:00 2001 From: Ed Leafe Date: Thu, 23 Dec 2010 16:56:21 -0600 Subject: updated the xenstore methods to reflect that they write to the param record of xenstore, not the actual xenstore itself. --- nova/virt/xenapi/vmops.py | 142 ++++++++++++++++++++++++++-------------------- 1 file changed, 82 insertions(+), 60 deletions(-) (limited to 'nova') diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py index 7bff47507..0e22ce306 100644 --- a/nova/virt/xenapi/vmops.py +++ b/nova/virt/xenapi/vmops.py @@ -18,7 +18,10 @@ Management class for VM-related functions (spawn, reboot, etc). """ +import json import logging +import random +import uuid from nova import db from nova import context @@ -53,13 +56,12 @@ class VMOps(object): """Create VM instance""" vm = VMHelper.lookup(self._session, instance.name) if vm is not None: - raise exception.Duplicate(_('Attempted to create' - ' non-unique name %s') % instance.name) + raise exception.Duplicate(_('Attempted to create non-unique name %s') + % instance.name) bridge = db.network_get_by_instance(context.get_admin_context(), instance['id'])['bridge'] - network_ref = \ - NetworkHelper.find_network_with_bridge(self._session, bridge) + network_ref = NetworkHelper.find_network_with_bridge(self._session, bridge) user = AuthManager().get_user(instance.user_id) project = AuthManager().get_project(instance.project_id) @@ -104,16 +106,6 @@ class VMOps(object): timer.f = _wait_for_boot return timer.start(interval=0.5, now=True) - def reboot(self, instance): - """Reboot VM instance""" - instance_name = instance.name - vm = VMHelper.lookup(self._session, instance_name) - if vm is None: - raise exception.NotFound(_('instance not' - ' found %s') % instance_name) - task = self._session.call_xenapi('Async.VM.clean_reboot', vm) - self._session.wait_for_task(instance.id, task) - def _get_vm_opaque_ref(self, instance_or_vm): try: instance_name = instance_or_vm.name @@ -122,15 +114,21 @@ class VMOps(object): # A vm opaque ref was passed vm = instance_or_vm if vm is None: - raise Exception('instance not present %s' % instance_name) + raise Exception(_('Instance not present %s') % instance_name) return vm + def reboot(self, instance): + """Reboot VM instance""" + vm = self._get_vm_opaque_ref(instance) + task = self._session.call_xenapi('Async.VM.clean_reboot', vm) + self._session.wait_for_task(instance.id, task) + def reset_root_password(self, instance): """Reset the root/admin password on the VM instance""" - self.add_to_xenstore(instance, {"reset_root_password": "requested"}) - self.add_to_xenstore(instance, {"TEST": "OMG!"}) + self.add_to_param_xenstore(instance, "reset_root_password", "requested") + self.add_to_param_xenstore(instance, "TEST", "OMG!") import time - self.add_to_xenstore(instance, {"timestamp": time.ctime()}) + self.add_to_param_xenstore(instance, "timestamp", time.ctime()) def destroy(self, instance): @@ -173,38 +171,27 @@ class VMOps(object): def pause(self, instance, callback): """Pause VM instance""" - instance_name = instance.name - vm = VMHelper.lookup(self._session, instance_name) - if vm is None: - raise exception.NotFound(_('Instance not' - ' found %s') % instance_name) + vm = self._get_vm_opaque_ref(instance) task = self._session.call_xenapi('Async.VM.pause', vm) self._wait_with_callback(instance.id, task, callback) def unpause(self, instance, callback): """Unpause VM instance""" - instance_name = instance.name - vm = VMHelper.lookup(self._session, instance_name) - if vm is None: - raise exception.NotFound(_('Instance not' - ' found %s') % instance_name) + vm = self._get_vm_opaque_ref(instance) task = self._session.call_xenapi('Async.VM.unpause', vm) self._wait_with_callback(instance.id, task, callback) def get_info(self, instance_id): """Return data about VM instance""" - vm = VMHelper.lookup(self._session, instance_id) + vm = VMHelper.lookup_blocking(self._session, instance_id) if vm is None: - raise exception.NotFound(_('Instance not' - ' found %s') % instance_id) + raise Exception(_('Instance not present %s') % instance_id) rec = self._session.get_xenapi().VM.get_record(vm) return VMHelper.compile_info(rec) def get_diagnostics(self, instance_id): """Return data about VM diagnostics""" - vm = VMHelper.lookup(self._session, instance_id) - if vm is None: - raise exception.NotFound(_("Instance not found %s") % instance_id) + vm = self._get_vm_opaque_ref(instance) rec = self._session.get_xenapi().VM.get_record(vm) return VMHelper.compile_diagnostics(self._session, rec) @@ -213,40 +200,75 @@ class VMOps(object): # TODO: implement this to fix pylint! return 'FAKE CONSOLE OUTPUT of instance' - def read_from_xenstore(self, instance_or_vm, keys=None): + def dh_keyinit(self, instance): + """Initiates a Diffie-Hellman (or, more precisely, a + Diffie-Hellman-Merkle) key exchange with the agent. It will + compute one side of the exchange and write it to xenstore. + When a response is received, it will then compute the shared + secret key, which is returned. + NOTE: the base and prime are pre-set; this may change in + the future. + """ + base = 5 + prime = 162259276829213363391578010288127 + secret_int = random.randint(100,1000) + val = (base ** secret_int) % prime + msgname = str(uuid.uuid4()) + key = "/data/host/%s" % msgname + self.add_to_param_xenstore(instance, key=key, + value={"name": "keyinit", "value": val}) + + def read_partial_from_param_xenstore(self, instance_or_vm, key_prefix): + """Returns a dict of all the keys in the xenstore for the given instance + that begin with the key_prefix. + """ + data = self.read_from_param_xenstore(instance_or_vm) + badkeys = [k for k in data.keys() + if not k.startswith(key_prefix)] + for badkey in badkeys: + del data[badkey] + return data + + def read_from_param_xenstore(self, instance_or_vm, keys=None): """Returns the xenstore data for the specified VM instance as - a dict. Accepts an optional list of keys; if the list of keys is - passed, the returned dict is filtered to only return the values + a dict. Accepts an optional key or list of keys; if a value for 'keys' + is passed, the returned dict is filtered to only return the values for those keys. """ vm = self._get_vm_opaque_ref(instance_or_vm) - ret = self._session.call_xenapi_request('VM.get_xenstore_data', (vm, )) - if keys: - allkeys = set(ret.keys()) - badkeys = allkeys.difference(keys) - for k in badkeys: - del ret[k] + data = self._session.call_xenapi_request('VM.get_xenstore_data', (vm, )) + ret = {} + if keys is None: + keys = data.keys() + elif isinstance(keys, basestring): + keys = [keys] + for key in keys: + raw = data.get(key) + if raw: + ret[key] = json.loads(raw) + else: + ret[key] = raw return ret - def add_to_xenstore(self, instance_or_vm, mapping): - """Takes a dict and adds it to the xenstore record for - the given vm instance. Existing data is preserved, but any - existing values for the mapping's keys are overwritten. - """ + def add_to_param_xenstore(self, instance_or_vm, key, val): + """Takes a key/value pair and adds it to the xenstore record + for the given vm instance. If the key exists in xenstore, it is + overwritten""" vm = self._get_vm_opaque_ref(instance_or_vm) - current_data = self.read_from_xenstore(vm) - current_data.update(mapping) - self.write_to_xenstore(vm, current_data) + self.remove_from_param_xenstore(instance_or_vm, key) + jsonval = json.dumps(val) + self._session.call_xenapi_request('VM.add_to_xenstore_data', + (vm, key, jsonval)) - def write_to_xenstore(self, instance_or_vm, mapping): - """Takes a dict and writes it to the xenstore record for - the given vm instance. Any existing data is overwritten. + def write_to_param_xenstore(self, instance_or_vm, mapping): + """Takes a dict and writes each key/value pair to the xenstore + record for the given vm instance. Any existing data for those + keys is overwritten. """ - vm = self._get_vm_opaque_ref(instance_or_vm) - self._session.call_xenapi_request('VM.set_xenstore_data', - (vm, mapping)) + for k, v in mapping.iteritems(): + self.add_to_param_xenstore(instance_or_vm, k, v) - def remove_from_xenstore(self, instance_or_vm, key_or_keys): + def remove_from_param_xenstore(self, instance_or_vm, key_or_keys): """Takes either a single key or a list of keys and removes them from the xenstore data for the given VM. If the key doesn't exist, the request is ignored. @@ -270,6 +292,6 @@ class VMOps(object): for key in keys: self._session.call_xenapi_request('VM.remove_from_xenstore_data', (vm, key)) - def clear_xenstore(self, instance_or_vm): + def clear_param_xenstore(self, instance_or_vm): """Removes all data from the xenstore record for this VM.""" - self.write_to_xenstore(instance_or_vm, {}) + self.write_to_param_xenstore(instance_or_vm, {}) -- cgit From c5c58cb20def79401a374f863983a343139b53f3 Mon Sep 17 00:00:00 2001 From: "NTT PF Lab." Date: Fri, 24 Dec 2010 20:38:49 +0900 Subject: Support IPv6 --- nova/api/ec2/cloud.py | 9 +++- nova/db/api.py | 9 ++++ nova/db/sqlalchemy/api.py | 23 ++++++++++ nova/db/sqlalchemy/models.py | 4 ++ nova/network/linux_net.py | 88 +++++++++++++++++++++++++++++++++++++ nova/network/manager.py | 30 +++++++++++-- nova/test.py | 3 +- nova/tests/api_unittest.py | 67 ++++++++++++++++++++++++++++ nova/tests/network_unittest.py | 21 +++++++++ nova/utils.py | 39 ++++++++++++++++ nova/virt/libvirt.qemu.xml.template | 1 + nova/virt/libvirt.uml.xml.template | 1 + nova/virt/libvirt_conn.py | 68 ++++++++++++++++++++++++---- 13 files changed, 349 insertions(+), 14 deletions(-) (limited to 'nova') diff --git a/nova/api/ec2/cloud.py b/nova/api/ec2/cloud.py index ebb13aedc..ff7a2f3cd 100644 --- a/nova/api/ec2/cloud.py +++ b/nova/api/ec2/cloud.py @@ -31,7 +31,7 @@ import time from nova import context import IPy - +import urllib from nova import crypto from nova import db from nova import exception @@ -307,6 +307,7 @@ class CloudController(object): values['group_id'] = source_security_group['id'] elif cidr_ip: # If this fails, it throws an exception. This is what we want. + cidr_ip = urllib.unquote(cidr_ip).decode() IPy.IP(cidr_ip) values['cidr'] = cidr_ip else: @@ -635,10 +636,16 @@ class CloudController(object): if instance['fixed_ip']['floating_ips']: fixed = instance['fixed_ip'] floating_addr = fixed['floating_ips'][0]['address'] + if instance['fixed_ip']['network'] and FLAGS.use_ipv6: + i['dnsNameV6'] = utils.to_global_ipv6( + instance['fixed_ip']['network']['cidr_v6'], + instance['mac_address']) + i['privateDnsName'] = fixed_addr i['publicDnsName'] = floating_addr i['dnsName'] = i['publicDnsName'] or i['privateDnsName'] i['keyName'] = instance['key_name'] + if context.user.is_admin(): i['keyName'] = '%s (%s, %s)' % (i['keyName'], instance['project_id'], diff --git a/nova/db/api.py b/nova/db/api.py index 8f9dc2443..caee0b7b8 100644 --- a/nova/db/api.py +++ b/nova/db/api.py @@ -240,6 +240,9 @@ def fixed_ip_get_instance(context, address): """Get an instance for a fixed ip by address.""" return IMPL.fixed_ip_get_instance(context, address) +def fixed_ip_get_instance_v6(context, address): + return IMPL.fixed_ip_get_instance_v6(context, address) + def fixed_ip_get_network(context, address): """Get a network for a fixed ip by address.""" @@ -298,6 +301,9 @@ def instance_get_fixed_address(context, instance_id): """Get the fixed ip address of an instance.""" return IMPL.instance_get_fixed_address(context, instance_id) +def instance_get_fixed_address_v6(context, instance_id): + return IMPL.instance_get_fixed_address_v6(context, instance_id) + def instance_get_floating_address(context, instance_id): """Get the first floating ip address of an instance.""" @@ -476,6 +482,9 @@ def project_get_network(context, project_id): """ return IMPL.project_get_network(context, project_id) +def project_get_network_v6(context, project_id): + return IMPL.project_get_network_v6(context, project_id) + ################### diff --git a/nova/db/sqlalchemy/api.py b/nova/db/sqlalchemy/api.py index 55036d1d1..29f3cdee1 100644 --- a/nova/db/sqlalchemy/api.py +++ b/nova/db/sqlalchemy/api.py @@ -504,6 +504,16 @@ def fixed_ip_get_instance(context, address): fixed_ip_ref = fixed_ip_get_by_address(context, address) return fixed_ip_ref.instance +@require_context +def fixed_ip_get_instance_v6(context, address): + session = get_session() + mac = utils.to_mac(address) + + result = session.query(models.Instance + ).filter_by(mac_address=mac + ).first() + return result + @require_admin_context def fixed_ip_get_network(context, address): @@ -692,6 +702,15 @@ def instance_get_fixed_address(context, instance_id): return None return instance_ref.fixed_ip['address'] +@require_context +def instance_get_fixed_address_v6(context, instance_id): + session = get_session() + with session.begin(): + instance_ref = instance_get(context, instance_id, session=session) + network_ref = project_get_network(context, context.project_id) + prefix = network_ref.cidr_v6 + mac = instance_ref.mac_address + return utils.to_global_ipv6(prefix, mac) @require_context def instance_get_floating_address(context, instance_id): @@ -1004,6 +1023,10 @@ def project_get_network(context, project_id): first() return rv +@require_context +def project_get_network_v6(context, project_id): + return project_get_network(context, project_id) + ################### diff --git a/nova/db/sqlalchemy/models.py b/nova/db/sqlalchemy/models.py index fe0a9a921..c81ef68f1 100644 --- a/nova/db/sqlalchemy/models.py +++ b/nova/db/sqlalchemy/models.py @@ -389,6 +389,10 @@ class Network(BASE, NovaBase): injected = Column(Boolean, default=False) cidr = Column(String(255), unique=True) + cidr_v6 = Column(String(255), unique=True) + + ra_server = Column(String(255)) + netmask = Column(String(255)) bridge = Column(String(255)) gateway = Column(String(255)) diff --git a/nova/network/linux_net.py b/nova/network/linux_net.py index 0fefd9415..a2ec731cd 100644 --- a/nova/network/linux_net.py +++ b/nova/network/linux_net.py @@ -53,6 +53,7 @@ flags.DEFINE_string('routing_source_ip', '127.0.0.1', flags.DEFINE_bool('use_nova_chains', False, 'use the nova_ routing chains instead of default') + DEFAULT_PORTS = [("tcp", 80), ("tcp", 22), ("udp", 1194), ("tcp", 443)] @@ -75,6 +76,11 @@ def init_host(): FLAGS.fixed_range) _confirm_rule("POSTROUTING", "-t nat -s %(range)s -d %(range)s -j ACCEPT" % {'range': FLAGS.fixed_range}) + if(FLAGS.use_ipv6): + _execute('sudo bash -c ' + + '"echo 1 > /proc/sys/net/ipv6/conf/all/forwarding"') + _execute('sudo bash -c ' + + '"echo 0 > /proc/sys/net/ipv6/conf/all/accept_ra"') def bind_floating_ip(floating_ip): @@ -158,6 +164,10 @@ def ensure_bridge(bridge, interface, net_attrs=None): net_attrs['gateway'], net_attrs['broadcast'], net_attrs['netmask'])) + if(FLAGS.use_ipv6): + _execute("sudo ifconfig %s add %s up" % \ + (bridge, + net_attrs['cidr_v6'])) else: _execute("sudo ifconfig %s up" % bridge) _confirm_rule("FORWARD", "--in-interface %s -j ACCEPT" % bridge) @@ -213,6 +223,50 @@ def update_dhcp(context, network_id): _execute(command, addl_env=env) +def update_ra(context, network_id): + network_ref = db.network_get(context, network_id) + + conffile = _ra_file(network_ref['bridge'], 'conf') + with open(conffile, 'w') as f: + conf_str = """ +interface %s +{ + AdvSendAdvert on; + MinRtrAdvInterval 3; + MaxRtrAdvInterval 10; + prefix %s + { + AdvOnLink on; + AdvAutonomous on; + }; +}; +""" % (network_ref['bridge'], network_ref['cidr_v6']) + f.write(conf_str) + + # Make sure dnsmasq can actually read it (it setuid()s to "nobody") + os.chmod(conffile, 0644) + + pid = _ra_pid_for(network_ref['bridge']) + + # if dnsmasq is already running, then tell it to reload + if pid: + out, _err = _execute('cat /proc/%d/cmdline' + % pid, check_exit_code=False) + if conffile in out: + try: + _execute('sudo kill -HUP %d' % pid) + return + except Exception as exc: # pylint: disable-msg=W0703 + logging.debug("Hupping radvd threw %s", exc) + else: + logging.debug("Pid %d is stale, relaunching radvd", pid) + command = _ra_cmd(network_ref) + _execute(command) + db.network_update(context, network_id, + {"ra_server": + utils.get_my_linklocal(network_ref['bridge'])}) + + def _host_dhcp(fixed_ip_ref): """Return a host string for an address""" instance_ref = fixed_ip_ref['instance'] @@ -268,6 +322,15 @@ def _dnsmasq_cmd(net): return ''.join(cmd) +def _ra_cmd(net): + """Builds dnsmasq command""" + cmd = ['sudo -E radvd', +# ' -u nobody', + ' -C %s' % _ra_file(net['bridge'], 'conf'), + ' -p %s' % _ra_file(net['bridge'], 'pid')] + return ''.join(cmd) + + def _stop_dnsmasq(network): """Stops the dnsmasq instance for a given network""" pid = _dnsmasq_pid_for(network) @@ -289,6 +352,16 @@ def _dhcp_file(bridge, kind): kind)) +def _ra_file(bridge, kind): + """Return path to a pid, leases or conf file for a bridge""" + + if not os.path.exists(FLAGS.networks_path): + os.makedirs(FLAGS.networks_path) + return os.path.abspath("%s/nova-ra-%s.%s" % (FLAGS.networks_path, + bridge, + kind)) + + def _dnsmasq_pid_for(bridge): """Returns the pid for prior dnsmasq instance for a bridge @@ -302,3 +375,18 @@ def _dnsmasq_pid_for(bridge): if os.path.exists(pid_file): with open(pid_file, 'r') as f: return int(f.read()) + + +def _ra_pid_for(bridge): + """Returns the pid for prior dnsmasq instance for a bridge + + Returns None if no pid file exists + + If machine has rebooted pid might be incorrect (caller should check) + """ + + pid_file = _ra_file(bridge, 'pid') + + if os.path.exists(pid_file): + with open(pid_file, 'r') as f: + return int(f.read()) diff --git a/nova/network/manager.py b/nova/network/manager.py index a7298b47f..ceea6966f 100644 --- a/nova/network/manager.py +++ b/nova/network/manager.py @@ -80,6 +80,7 @@ flags.DEFINE_integer('network_size', 256, flags.DEFINE_string('floating_range', '4.4.4.0/24', 'Floating IP address block') flags.DEFINE_string('fixed_range', '10.0.0.0/8', 'Fixed IP address block') +flags.DEFINE_string('fixed_range_v6', 'fd00::/48', 'Fixed IPv6 address block') flags.DEFINE_integer('cnt_vpn_clients', 5, 'Number of addresses reserved for vpn clients') flags.DEFINE_string('network_driver', 'nova.network.linux_net', @@ -88,6 +89,8 @@ flags.DEFINE_bool('update_dhcp_on_disassociate', False, 'Whether to update dhcp when fixed_ip is disassociated') flags.DEFINE_integer('fixed_ip_disassociate_timeout', 600, 'Seconds after which a deallocated ip is disassociated') +flags.DEFINE_bool('use_ipv6', True, + 'use the ipv6') class AddressAlreadyAllocated(exception.Error): @@ -217,7 +220,7 @@ class NetworkManager(manager.Manager): """Get the network for the current context.""" raise NotImplementedError() - def create_networks(self, context, num_networks, network_size, + def create_networks(self, context, num_networks, network_size, cidr_v6, *args, **kwargs): """Create networks based on parameters.""" raise NotImplementedError() @@ -307,9 +310,11 @@ class FlatManager(NetworkManager): pass def create_networks(self, context, cidr, num_networks, network_size, - *args, **kwargs): + cidr_v6, *args, **kwargs): """Create networks based on parameters.""" fixed_net = IPy.IP(cidr) + fixed_net_v6 = IPy.IP(cidr_v6) + significant_bits_v6 = 64 for index in range(num_networks): start = index * network_size significant_bits = 32 - int(math.log(network_size, 2)) @@ -322,7 +327,13 @@ class FlatManager(NetworkManager): net['gateway'] = str(project_net[1]) net['broadcast'] = str(project_net.broadcast()) net['dhcp_start'] = str(project_net[2]) + + if(FLAGS.use_ipv6): + cidr_v6 = "%s/%s" % (fixed_net_v6[0], significant_bits_v6) + net['cidr_v6'] = cidr_v6 + network_ref = self.db.network_create_safe(context, net) + if network_ref: self._create_fixed_ips(context, network_ref['id']) @@ -466,12 +477,16 @@ class VlanManager(NetworkManager): pass def create_networks(self, context, cidr, num_networks, network_size, - vlan_start, vpn_start): + vlan_start, vpn_start, cidr_v6): """Create networks based on parameters.""" fixed_net = IPy.IP(cidr) + fixed_net_v6 = IPy.IP(cidr_v6) + network_size_v6 = 1 << 64 + significant_bits_v6 = 64 for index in range(num_networks): vlan = vlan_start + index start = index * network_size + start_v6 = index * network_size_v6 significant_bits = 32 - int(math.log(network_size, 2)) cidr = "%s/%s" % (fixed_net[start], significant_bits) project_net = IPy.IP(cidr) @@ -484,6 +499,13 @@ class VlanManager(NetworkManager): net['dhcp_start'] = str(project_net[3]) net['vlan'] = vlan net['bridge'] = 'br%s' % vlan + if(FLAGS.use_ipv6): + cidr_v6 = "%s/%s" % ( + fixed_net_v6[start_v6], + significant_bits_v6 + ) + net['cidr_v6'] = cidr_v6 + # NOTE(vish): This makes ports unique accross the cloud, a more # robust solution would be to make them unique per ip net['vpn_public_port'] = vpn_start + index @@ -506,6 +528,8 @@ class VlanManager(NetworkManager): network_ref['bridge'], network_ref) self.driver.update_dhcp(context, network_id) + if(FLAGS.use_ipv6): + self.driver.update_ra(context, network_id) @property def _bottom_reserved_ips(self): diff --git a/nova/test.py b/nova/test.py index 5c2a72819..ee2fc2720 100644 --- a/nova/test.py +++ b/nova/test.py @@ -70,7 +70,8 @@ class TrialTestCase(unittest.TestCase): FLAGS.fixed_range, 5, 16, FLAGS.vlan_start, - FLAGS.vpn_start) + FLAGS.vpn_start, + FLAGS.fixed_range_v6) # emulate some of the mox stuff, we can't use the metaclass # because it screws with our generators diff --git a/nova/tests/api_unittest.py b/nova/tests/api_unittest.py index 33d4cb294..a508235c4 100644 --- a/nova/tests/api_unittest.py +++ b/nova/tests/api_unittest.py @@ -24,6 +24,7 @@ import httplib import random import StringIO import webob +import logging from nova import context from nova import flags @@ -265,6 +266,72 @@ class ApiEc2TestCase(test.TrialTestCase): return + def test_authorize_revoke_security_group_cidr_v6(self): + """ + Test that we can add and remove CIDR based rules + to a security group for IPv6 + """ + self.expect_http() + self.mox.ReplayAll() + user = self.manager.create_user('fake', 'fake', 'fake') + project = self.manager.create_project('fake', 'fake', 'fake') + + # At the moment, you need both of these to actually be netadmin + self.manager.add_role('fake', 'netadmin') + project.add_role('fake', 'netadmin') + + security_group_name = "".join(random.choice("sdiuisudfsdcnpaqwertasd") + for x in range(random.randint(4, 8))) + + group = self.ec2.create_security_group(security_group_name, + 'test group') + + self.expect_http() + self.mox.ReplayAll() + group.connection = self.ec2 + + group.authorize('tcp', 80, 81, '::/0') + + self.expect_http() + self.mox.ReplayAll() + + rv = self.ec2.get_all_security_groups() + # I don't bother checkng that we actually find it here, + # because the create/delete unit test further up should + # be good enough for that. + for group in rv: + if group.name == security_group_name: + self.assertEquals(len(group.rules), 1) + self.assertEquals(int(group.rules[0].from_port), 80) + self.assertEquals(int(group.rules[0].to_port), 81) + self.assertEquals(len(group.rules[0].grants), 1) + self.assertEquals(str(group.rules[0].grants[0]), '::/0') + + self.expect_http() + self.mox.ReplayAll() + group.connection = self.ec2 + + group.revoke('tcp', 80, 81, '::/0') + + self.expect_http() + self.mox.ReplayAll() + + self.ec2.delete_security_group(security_group_name) + + self.expect_http() + self.mox.ReplayAll() + group.connection = self.ec2 + + rv = self.ec2.get_all_security_groups() + + self.assertEqual(len(rv), 1) + self.assertEqual(rv[0].name, 'default') + + self.manager.delete_project(project) + self.manager.delete_user(user) + + return + def test_authorize_revoke_security_group_foreign_group(self): """ Test that we can grant and revoke another security group access diff --git a/nova/tests/network_unittest.py b/nova/tests/network_unittest.py index 6f4705719..0a4b50e96 100644 --- a/nova/tests/network_unittest.py +++ b/nova/tests/network_unittest.py @@ -97,6 +97,27 @@ class NetworkTestCase(test.TrialTestCase): self.context.project_id = self.projects[project_num].id self.network.deallocate_fixed_ip(self.context, address) + def test_private_ipv6(self): + """Make sure ipv6 is OK""" + if FLAGS.use_ipv6: + instance_ref = self._create_instance(1) + network_ref = db.project_get_network( + self.context, + self.context.project_id) + address_v6 = db.instance_get_fixed_address_v6( + self.context, + instance_ref['id']) + self.assertEqual(instance_ref['mac_address'], + utils.to_mac(address_v6)) + instance_ref2 = db.fixed_ip_get_instance_v6( + self.context, + address_v6) + self.assertEqual(instance_ref['id'], instance_ref2['id']) + self.assertEqual(address_v6, + utils.to_global_ipv6( + network_ref['cidr_v6'], + instance_ref['mac_address'])) + def test_public_network_association(self): """Makes sure that we can allocaate a public ip""" # TODO(vish): better way of adding floating ips diff --git a/nova/utils.py b/nova/utils.py index 142584df8..211a2cb75 100644 --- a/nova/utils.py +++ b/nova/utils.py @@ -30,6 +30,8 @@ import subprocess import socket import sys from xml.sax import saxutils +import re +import netaddr from twisted.internet.threads import deferToThread @@ -45,10 +47,14 @@ TIME_FORMAT = "%Y-%m-%dT%H:%M:%SZ" def import_class(import_str): """Returns a class from a string including module and class""" mod_str, _sep, class_str = import_str.rpartition('.') + logging.debug(import_str) try: __import__(mod_str) return getattr(sys.modules[mod_str], class_str) except (ImportError, ValueError, AttributeError): + logging.debug(ImportError) + logging.debug(ValueError) + logging.debug(AttributeError) raise exception.NotFound('Class %s cannot be found' % class_str) @@ -165,6 +171,39 @@ def get_my_ip(): return "127.0.0.1" +def get_my_linklocal(interface): + if getattr(FLAGS, 'fake_tests', None): + return 'fe00::' + try: + if_str = execute("ifconfig %s" % interface) + condition = "\s+inet6\s+addr:\s+([0-9a-f:]+/\d+)\s+Scope:Link" + links = [re.search(condition, x) for x in if_str[0].split('\n')] + address = [w.group(1) for w in links if w is not None] + if address[0] is not None: + return address[0] + else: + return None + except RuntimeError as ex: + logging.warn("Couldn't get Link Local IP of %s :%s", interface, ex) + return None + + +def to_global_ipv6(prefix, mac): + mac64 = netaddr.EUI(mac).eui64().words + int_addr = int(''.join(['%02x' % i for i in mac64]), 16) + mac64_addr = netaddr.IPAddress(int_addr) + maskIP = netaddr.IPNetwork(prefix).ip + return (mac64_addr ^ netaddr.IPAddress('::0200:0:0:0') | maskIP).format() + + +def to_mac(ipv6_address): + address = netaddr.IPAddress(ipv6_address) + mask1 = netaddr.IPAddress("::ffff:ffff:ffff:ffff") + mask2 = netaddr.IPAddress("::0200:0:0:0") + mac64 = netaddr.EUI(int(address & mask1 ^ mask2)).words + return ":".join(["%02x" % i for i in mac64[0:3] + mac64[5:8]]) + + def isotime(at=None): if not at: at = datetime.datetime.utcnow() diff --git a/nova/virt/libvirt.qemu.xml.template b/nova/virt/libvirt.qemu.xml.template index 2538b1ade..0ffe17e8d 100644 --- a/nova/virt/libvirt.qemu.xml.template +++ b/nova/virt/libvirt.qemu.xml.template @@ -23,6 +23,7 @@ + diff --git a/nova/virt/libvirt.uml.xml.template b/nova/virt/libvirt.uml.xml.template index bb8b47911..0d355b81c 100644 --- a/nova/virt/libvirt.uml.xml.template +++ b/nova/virt/libvirt.uml.xml.template @@ -17,6 +17,7 @@ + diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py index 18085089f..71d9f781d 100644 --- a/nova/virt/libvirt_conn.py +++ b/nova/virt/libvirt_conn.py @@ -514,6 +514,8 @@ class LibvirtConnection(object): instance['id']) # Assume that the gateway also acts as the dhcp server. dhcp_server = network['gateway'] + #TODO ipv6 + ra_server = network['ra_server'] xml_info = {'type': FLAGS.libvirt_type, 'name': instance['name'], 'basepath': os.path.join(FLAGS.instances_path, @@ -523,11 +525,13 @@ class LibvirtConnection(object): 'bridge_name': network['bridge'], 'mac_address': instance['mac_address'], 'ip_address': ip_address, - 'dhcp_server': dhcp_server} + 'dhcp_server': dhcp_server, + 'ra_server': ra_server} if rescue: libvirt_xml = self.rescue_xml % xml_info else: libvirt_xml = self.libvirt_xml % xml_info + logging.debug('instance %s: finished toXML method', instance['name']) return libvirt_xml @@ -701,6 +705,7 @@ class NWFilterFirewall(object): + ''' @@ -722,6 +727,14 @@ class NWFilterFirewall(object): ''' + nova_ra_filter = ''' + d707fa71-4fb5-4b27-9ab7-ba5ca19c8804 + + + + ''' + def nova_base_ipv4_filter(self): retval = "" for protocol in ['tcp', 'udp', 'icmp']: @@ -736,13 +749,13 @@ class NWFilterFirewall(object): def nova_base_ipv6_filter(self): retval = "" - for protocol in ['tcp', 'udp', 'icmp']: + for protocol in ['tcp-ipv6', 'udp-ipv6', 'icmpv6']: for direction, action, priority in [('out', 'accept', 399), ('inout', 'drop', 400)]: retval += """ - <%s-ipv6 /> + <%s /> """ % (action, direction, - priority, protocol) + priority, protocol) retval += '' return retval @@ -755,6 +768,15 @@ class NWFilterFirewall(object): retval += '' return retval + def nova_project_filter_v6(self, project, net, mask): + retval = "" % project + for protocol in ['tcp-ipv6', 'udp-ipv6', 'icmpv6']: + retval += """ + <%s srcipaddr='%s' srcipmask='%s' /> + """ % (protocol, net, mask) + retval += '' + return retval + def _define_filter(self, xml): if callable(xml): xml = xml() @@ -766,6 +788,11 @@ class NWFilterFirewall(object): net = IPy.IP(cidr) return str(net.net()), str(net.netmask()) + @staticmethod + def _get_ip_version(cidr): + net = IPy.IP(cidr) + return int(net.version()) + @defer.inlineCallbacks def setup_nwfilters_for_instance(self, instance): """ @@ -777,6 +804,7 @@ class NWFilterFirewall(object): yield self._define_filter(self.nova_base_ipv4_filter) yield self._define_filter(self.nova_base_ipv6_filter) yield self._define_filter(self.nova_dhcp_filter) + yield self._define_filter(self.nova_ra_filter) yield self._define_filter(self.nova_base_filter) nwfilter_xml = "\n" \ @@ -787,12 +815,22 @@ class NWFilterFirewall(object): network_ref = db.project_get_network(context.get_admin_context(), instance['project_id']) net, mask = self._get_net_and_mask(network_ref['cidr']) + if(FLAGS.use_ipv6): + net_v6, mask_v6 = self._get_net_and_mask( + network_ref['cidr_v6']) project_filter = self.nova_project_filter(instance['project_id'], net, mask) yield self._define_filter(project_filter) - nwfilter_xml += " \n" % \ instance['project_id'] + if(FLAGS.use_ipv6): + project_filter_v6 = self.nova_project_filter_v6( + instance['project_id'], + net_v6, mask_v6) + yield self._define_filter(project_filter_v6) + nwfilter_xml += \ + " \n" % \ + instance['project_id'] for security_group in instance.security_groups: yield self.ensure_security_group_filter(security_group['id']) @@ -812,12 +850,21 @@ class NWFilterFirewall(object): security_group = db.security_group_get(context.get_admin_context(), security_group_id) rule_xml = "" + version = 4 + v6protocol = {'tcp':'tcp-ipv6', 'udp':'udp-ipv6', 'icmp':'icmpv6'} for rule in security_group.rules: rule_xml += "" if rule.cidr: + version = self._get_ip_version(rule.cidr) net, mask = self._get_net_and_mask(rule.cidr) - rule_xml += "<%s srcipaddr='%s' srcipmask='%s' " % \ - (rule.protocol, net, mask) + if(FLAGS.use_ipv6 and version == 6): + rule_xml += "<%s " % v6protocol[rule.protocol] + rule_xml += "srcipaddr='%s' " % net + rule_xml += "srcipmask='%s' " % mask + else: + rule_xml += "<%s " % rule.protocol + rule_xml += "srcipaddr='%s' " % net + rule_xml += "srcipmask='%s' " % mask if rule.protocol in ['tcp', 'udp']: rule_xml += "dstportstart='%s' dstportend='%s' " % \ (rule.from_port, rule.to_port) @@ -832,6 +879,9 @@ class NWFilterFirewall(object): rule_xml += '/>\n' rule_xml += "\n" - xml = "%s" % \ - (security_group_id, rule_xml,) + xml = " Date: Fri, 31 Dec 2010 05:37:30 -0600 Subject: Before merge with xenstore-plugin code --- nova/compute/manager.py | 19 ++- nova/virt/xenapi/vmops.py | 290 ++++++++++++++++++++++++++++++++++++++-------- nova/virt/xenapi_conn.py | 18 ++- 3 files changed, 267 insertions(+), 60 deletions(-) (limited to 'nova') diff --git a/nova/compute/manager.py b/nova/compute/manager.py index c46587adc..4d8c5e1a5 100644 --- a/nova/compute/manager.py +++ b/nova/compute/manager.py @@ -36,6 +36,7 @@ terminating it. import datetime import logging +import string from nova import exception from nova import flags @@ -239,15 +240,21 @@ class ComputeManager(manager.Manager): power_state.RUNNING) logging.debug('instance %s: resetting root password', - instance_ref['name']) - self.db.instance_set_state(context, - instance_id, - power_state.NOSTATE, - 'resetting_password') + instance_ref['name']) + self.db.instance_set_state(context, instance_id, + power_state.NOSTATE, 'resetting_password') #### TODO: (dabo) not sure how we will implement this yet. - self.driver.reset_root_password(instance_ref) + new_pass = self._generate_password(12) + self.driver.reset_root_password(instance_ref, new_pass) self._update_state(context, instance_id) + def _generate_password(self, length=20): + """Generate a random sequence of letters and digits + to be used as a password. + """ + chrs = string.letters + string.digits + return "".join([random.choice(chrs) for i in xrange(length)]) + @exception.wrap_exception def rescue_instance(self, context, instance_id): """Rescue an instance on this server.""" diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py index 3f8f0da69..304ff7232 100644 --- a/nova/virt/xenapi/vmops.py +++ b/nova/virt/xenapi/vmops.py @@ -20,7 +20,10 @@ Management class for VM-related functions (spawn, reboot, etc). import json import logging +import os import random +import subprocess +import tempfile import uuid from nova import db @@ -138,14 +141,38 @@ class VMOps(object): task = self._session.call_xenapi('Async.VM.clean_reboot', vm) self._session.wait_for_task(instance.id, task) - def reset_root_password(self, instance): - """Reset the root/admin password on the VM instance""" - self.add_to_param_xenstore(instance, "reset_root_password", "requested") - self.add_to_param_xenstore(instance, "TEST", "OMG!") - import time - self.add_to_param_xenstore(instance, "timestamp", time.ctime()) - - + def reset_root_password(self, instance, new_pass): + """Reset the root/admin password on the VM instance. This is + done via an agent running on the VM. Communication between + nova and the agent is done via writing xenstore records. Since + communication is done over the XenAPI RPC calls, we need to + encrypt the password. We're using a simple Diffie-Hellman class + instead of the more advanced one in M2Crypto for compatibility + with the agent code. + """ + # Need to uniquely identify this request. + transaction_id = str(uuid.uuid4()) + # The simple Diffie-Hellman class is used to manage key exchange. + dh = SimpleDH() + args = {'id': transaction_id, 'pub': str(dh.get_public())} + resp = self._make_agent_call('key_init', instance, '', args) + resp_dict = json.loads(resp) + # Successful return code from key_init is 'D0' + if resp_dict['returncode'] != 'D0': + # There was some sort of error + raise RuntimeError(resp_dict['message']) + agent_pub = int(resp_dict['message']) + dh.compute_shared(agent_pub) + enc_pass = dh.encrypt(new_pass) + # Send the encrypted password + args['enc_pass'] = enc_pass + resp = self._make_agent_call('password', instance, '', args) + resp_dict = json.loads(resp) + # Successful return code from password is '0' + if resp_dict['returncode'] != '0': + raise RuntimeError(resp_dict['message']) + return resp_dict['message'] + def destroy(self, instance): """Destroy VM instance""" vm = VMHelper.lookup(self._session, instance.name) @@ -159,7 +186,7 @@ class VMOps(object): task = self._session.call_xenapi('Async.VM.hard_shutdown', vm) self._session.wait_for_task(instance.id, task) - except XenAPI.Failure, exc: + except self.XenAPI.Failure, exc: logging.warn(exc) # Disk clean-up if vdis: @@ -167,20 +194,20 @@ class VMOps(object): try: task = self._session.call_xenapi('Async.VDI.destroy', vdi) self._session.wait_for_task(instance.id, task) - except XenAPI.Failure, exc: + except self.XenAPI.Failure, exc: logging.warn(exc) # VM Destroy try: task = self._session.call_xenapi('Async.VM.destroy', vm) self._session.wait_for_task(instance.id, task) - except XenAPI.Failure, exc: + except self.XenAPI.Failure, exc: logging.warn(exc) def _wait_with_callback(self, instance_id, task, callback): ret = None try: ret = self._session.wait_for_task(instance_id, task) - except XenAPI.Failure, exc: + except self.XenAPI.Failure, exc: logging.warn(exc) callback(ret) @@ -235,27 +262,119 @@ class VMOps(object): # TODO: implement this to fix pylint! return 'FAKE CONSOLE OUTPUT of instance' - def dh_keyinit(self, instance): - """Initiates a Diffie-Hellman (or, more precisely, a - Diffie-Hellman-Merkle) key exchange with the agent. It will - compute one side of the exchange and write it to xenstore. - When a response is received, it will then compute the shared - secret key, which is returned. - NOTE: the base and prime are pre-set; this may change in - the future. + def list_from_xenstore(self, vm, path): + """Runs the xenstore-ls command to get a listing of all records + from 'path' downward. Returns a dict with the sub-paths as keys, + and the value stored in those paths as values. If nothing is + found at that path, returns None. + """ + ret = self._make_xenstore_call('list_records', vm, path) + try: + return json.loads(ret) + except ValueError: + # Not a valid JSON value + return ret + + def read_from_xenstore(self, vm, path): + """Returns the value stored in the xenstore record for the given VM + at the specified location. A XenAPIPlugin.PluginError will be raised + if any error is encountered in the read process. + """ + try: + ret = self._make_xenstore_call('read_record', vm, path, + {'ignore_missing_path': 'True'}) + except self.XenAPI.Failure, e: + print "XENERR", e + return None + except StandardError, e: + print "ERR", type(e), e, e.msg + return None + try: + return json.loads(ret) + except ValueError: + # Not a JSON object + if ret == "None": + # Can't marshall None over RPC calls. + return None + return ret + + def write_to_xenstore(self, vm, path, value): + """Writes the passed value to the xenstore record for the given VM + at the specified location. A XenAPIPlugin.PluginError will be raised + if any error is encountered in the write process. + """ + return self._make_xenstore_call('write_record', vm, path, {'value': json.dumps(value)}) + + def clear_xenstore(self, vm, path): + """Deletes the VM's xenstore record for the specified path. + If there is no such record, the request is ignored. + """ + self._make_xenstore_call('delete_record', vm, path) + + def _make_xenstore_call(self, method, vm, path, addl_args={}): + """Abstracts out the interaction with the xenstore xenapi plugin.""" + return self._make_plugin_call('xenstore.py', method=method, vm=vm, path=path, + addl_args=addl_args) + + def _make_agent_call(self, method, vm, path, addl_args={}): + """Abstracts out the interaction with the agent xenapi plugin.""" + return self._make_plugin_call('agent.py', method=method, vm=vm, path=path, + addl_args=addl_args) + + def _make_plugin_call(self, plugin, method, vm, path, addl_args={}): + vm = self._get_vm_opaque_ref(vm) + rec = self._session.get_xenapi().VM.get_record(vm) + args = {'dom_id': rec['domid'], 'path': path} + args.update(addl_args) + # If the 'testing_mode' attribute is set, add that to the args. + if getattr(self, 'testing_mode', False): + args['testing_mode'] = 'true' + try: + task = self._session.async_call_plugin(plugin, method, args) + ret = self._session.wait_for_task(0, task) + except self.XenAPI.Failure, e: + raise RuntimeError("%s" % e.details[-1]) + return ret + + def add_to_xenstore(self, vm, path, key, value): + """Adds the passed key/value pair to the xenstore record for + the given VM at the specified location. A XenAPIPlugin.PluginError + will be raised if any error is encountered in the write process. """ - base = 5 - prime = 162259276829213363391578010288127 - secret_int = random.randint(100,1000) - val = (base ** secret_int) % prime - msgname = str(uuid.uuid4()) - key = "/data/host/%s" % msgname - self.add_to_param_xenstore(instance, key=key, - value={"name": "keyinit", "value": val}) + current = self.read_from_xenstore(vm, path) + current[key] = value + self.write_to_xenstore(vm, path, current) + def remove_from_xenstore(self, vm, path, key_or_keys): + """Takes either a single key or a list of keys and removes + them from the xenstoreirecord data for the given VM. + If the key doesn't exist, the request is ignored. + """ + current = self.list_from_xenstore(vm, path) + if not current: + return + if isinstance(key_or_keys, basestring): + keys = [key_or_keys] + else: + keys = key_or_keys + keys.sort(lambda x,y: cmp(y.count('/'), x.count('/'))) + for key in keys: + if path: + keypath = "%s/%s" % (path, key) + else: + keypath = key + self._make_xenstore_call('delete_record', vm, keypath) + + + ######################################################################## + ###### The following methods interact with the xenstore parameter + ###### record, not the live xenstore. They were created before I + ###### knew the difference, and are left in here in case they prove + ###### to be useful. + ######################################################################## def read_partial_from_param_xenstore(self, instance_or_vm, key_prefix): - """Returns a dict of all the keys in the xenstore for the given instance - that begin with the key_prefix. + """Returns a dict of all the keys in the xenstore parameter record + for the given instance that begin with the key_prefix. """ data = self.read_from_param_xenstore(instance_or_vm) badkeys = [k for k in data.keys() @@ -265,8 +384,8 @@ class VMOps(object): return data def read_from_param_xenstore(self, instance_or_vm, keys=None): - """Returns the xenstore data for the specified VM instance as - a dict. Accepts an optional key or list of keys; if a value for 'keys' + """Returns the xenstore parameter record data for the specified VM instance + as a dict. Accepts an optional key or list of keys; if a value for 'keys' is passed, the returned dict is filtered to only return the values for those keys. """ @@ -286,9 +405,9 @@ class VMOps(object): return ret def add_to_param_xenstore(self, instance_or_vm, key, val): - """Takes a key/value pair and adds it to the xenstore record - for the given vm instance. If the key exists in xenstore, it is - overwritten""" + """Takes a key/value pair and adds it to the xenstore parameter + record for the given vm instance. If the key exists in xenstore, + it is overwritten""" vm = self._get_vm_opaque_ref(instance_or_vm) self.remove_from_param_xenstore(instance_or_vm, key) jsonval = json.dumps(val) @@ -297,27 +416,16 @@ class VMOps(object): def write_to_param_xenstore(self, instance_or_vm, mapping): """Takes a dict and writes each key/value pair to the xenstore - record for the given vm instance. Any existing data for those - keys is overwritten. + parameter record for the given vm instance. Any existing data for + those keys is overwritten. """ for k, v in mapping.iteritems(): self.add_to_param_xenstore(instance_or_vm, k, v) def remove_from_param_xenstore(self, instance_or_vm, key_or_keys): """Takes either a single key or a list of keys and removes - them from the xenstore data for the given VM. If the key - doesn't exist, the request is ignored. - """ - vm = self._get_vm_opaque_ref(instance_or_vm) - if isinstance(key_or_keys, basestring): - keys = [key_or_keys] - else: - keys = key_or_keys - for key in keys: - self._session.call_xenapi_request('VM.remove_from_xenstore_data', (vm, key)) - """Takes either a single key or a list of keys and removes - them from the xenstore data for the given VM. If the key - doesn't exist, the request is ignored. + them from the xenstore parameter record data for the given VM. + If the key doesn't exist, the request is ignored. """ vm = self._get_vm_opaque_ref(instance_or_vm) if isinstance(key_or_keys, basestring): @@ -328,5 +436,85 @@ class VMOps(object): self._session.call_xenapi_request('VM.remove_from_xenstore_data', (vm, key)) def clear_param_xenstore(self, instance_or_vm): - """Removes all data from the xenstore record for this VM.""" + """Removes all data from the xenstore parameter record for this VM.""" self.write_to_param_xenstore(instance_or_vm, {}) + ######################################################################## + + +def _runproc(cmd): + pipe = subprocess.PIPE + return subprocess.Popen([cmd], shell=True, stdin=pipe, stdout=pipe, stderr=pipe, close_fds=True) + +class SimpleDH(object): + """This class wraps all the functionality needed to implement + basic Diffie-Hellman-Merkle key exchange in Python. It features + intelligent defaults for the prime and base numbers needed for the + calculation, while allowing you to supply your own. It requires that + the openssl binary be installed on the system on which this is run, + as it uses that to handle the encryption and decryption. If openssl + is not available, a RuntimeError will be raised. + """ +# def __init__(self, prime=None, base=None): +# """You can specify the values for prime and base if you wish; +# otherwise, reasonable default values will be used. +# """ +# if prime is None: +# self._prime = 162259276829213363391578010288127 +# else: +# self._prime = prime +# if base is None: +# self._base = 5 +# else: +# self._base = base +# self._secret = random.randint(5000, 15000) +# self._shared = self._public = None + def __init__(self, prime=None, base=None, secret=None): + """You can specify the values for prime and base if you wish; + otherwise, reasonable default values will be used. + """ + if prime is None: + self._prime = 162259276829213363391578010288127 + else: + self._prime = prime + if base is None: + self._base = 5 + else: + self._base = base + if secret is None: + self._secret = random.randint(5000, 15000) + else: + self._secret = secret + self._shared = self._public = None + + def get_public(self): + self._public = (self._base ** self._secret) % self._prime + return self._public + + def compute_shared(self, other): + self._shared = (other ** self._secret) % self._prime + return self._shared + + def _run_ssl(self, text, which): + base_cmd = ('cat %(tmpfile)s | openssl enc -aes-128-cbc ' + '-a -pass pass:%(shared)s -nosalt %(dec_flag)s') + if which.lower()[0] == 'd': + dec_flag = ' -d' + else: + dec_flag = '' + fd, tmpfile = tempfile.mkstemp() + os.close(fd) + file(tmpfile, 'w').write(text) + shared = self._shared + cmd = base_cmd % locals() + proc = _runproc(cmd) + proc.wait() + err = proc.stderr.read() + if err: + raise RuntimeError(_('OpenSSL error: %s') % err) + return proc.stdout.read() + + def encrypt(self, text): + return self._run_ssl(text, 'enc') + + def decrypt(self, text): + return self._run_ssl(text, 'dec') diff --git a/nova/virt/xenapi_conn.py b/nova/virt/xenapi_conn.py index 7fb7ff15d..6b65a8c48 100644 --- a/nova/virt/xenapi_conn.py +++ b/nova/virt/xenapi_conn.py @@ -135,9 +135,9 @@ class XenAPIConnection(object): """Reboot VM instance""" self._vmops.reboot(instance) - def reset_root_password(self, instance): + def reset_root_password(self, instance, new_pass): """Reset the root/admin password on the VM instance""" - self._vmops.reset_root_password(instance) + self._vmops.reset_root_password(instance, new_pass) def destroy(self, instance): """Destroy VM instance""" @@ -264,7 +264,19 @@ class XenAPISession(object): status, error_info)) done.send_exception(self.XenAPI.Failure(error_info)) - db.instance_action_create(context.get_admin_context(), action) + + #db.instance_action_create(context.get_admin_context(), action) + import sqlalchemy + from sqlalchemy.exc import IntegrityError as IntegrityError + try: + db.instance_action_create(context.get_admin_context(), action) + except IntegrityError: + # Some methods don't pass unique IDs, so the call to + # instance_action_create() will raise IntegrityErrors. Rather + # than bomb out, I'm explicitly silencing them so that the + # code can continue to work until they fix that method. + pass + except self.XenAPI.Failure, exc: logging.warn(exc) done.send_exception(*sys.exc_info()) -- cgit From 4f77545cb1ae58484669028fbddb06592b1ee7e4 Mon Sep 17 00:00:00 2001 From: Ed Leafe Date: Fri, 31 Dec 2010 08:54:10 -0600 Subject: fixed pep8 issues --- nova/virt/xenapi/vmops.py | 64 +++++++++++++++++++++-------------------------- nova/virt/xenapi_conn.py | 20 +++++++-------- 2 files changed, 39 insertions(+), 45 deletions(-) (limited to 'nova') diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py index b0269d960..49857e3d9 100644 --- a/nova/virt/xenapi/vmops.py +++ b/nova/virt/xenapi/vmops.py @@ -61,12 +61,13 @@ class VMOps(object): """Create VM instance""" vm = VMHelper.lookup(self._session, instance.name) if vm is not None: - raise exception.Duplicate(_('Attempted to create non-unique name %s') - % instance.name) + msg = _('Attempted to create non-unique name %s') % instance.name) + raise exception.Duplicate( bridge = db.network_get_by_instance(context.get_admin_context(), instance['id'])['bridge'] - network_ref = NetworkHelper.find_network_with_bridge(self._session, bridge) + network_ref = NetworkHelper.find_network_with_bridge(self._session, + bridge) user = AuthManager().get_user(instance.user_id) project = AuthManager().get_project(instance.project_id) @@ -222,7 +223,7 @@ class VMOps(object): if resp_dict['returncode'] != '0': raise RuntimeError(resp_dict['message']) return resp_dict['message'] - + def destroy(self, instance): """Destroy VM instance""" vm = VMHelper.lookup(self._session, instance.name) @@ -321,7 +322,7 @@ class VMOps(object): def list_from_xenstore(self, vm, path): """Runs the xenstore-ls command to get a listing of all records from 'path' downward. Returns a dict with the sub-paths as keys, - and the value stored in those paths as values. If nothing is + and the value stored in those paths as values. If nothing is found at that path, returns None. """ ret = self._make_xenstore_call('list_records', vm, path) @@ -355,7 +356,8 @@ class VMOps(object): at the specified location. A XenAPIPlugin.PluginError will be raised if any error is encountered in the write process. """ - return self._make_xenstore_call('write_record', vm, path, {'value': json.dumps(value)}) + return self._make_xenstore_call('write_record', vm, path, + {'value': json.dumps(value)}) def clear_xenstore(self, vm, path): """Deletes the VM's xenstore record for the specified path. @@ -365,13 +367,13 @@ class VMOps(object): def _make_xenstore_call(self, method, vm, path, addl_args={}): """Handles calls to the xenstore xenapi plugin.""" - return self._make_plugin_call('xenstore.py', method=method, vm=vm, path=path, - addl_args=addl_args) + return self._make_plugin_call('xenstore.py', method=method, vm=vm, + path=path, addl_args=addl_args) def _make_agent_call(self, method, vm, path, addl_args={}): """Abstracts out the interaction with the agent xenapi plugin.""" - return self._make_plugin_call('agent.py', method=method, vm=vm, path=path, - addl_args=addl_args) + return self._make_plugin_call('agent.py', method=method, vm=vm, + path=path, addl_args=addl_args) def _make_plugin_call(self, plugin, method, vm, path, addl_args={}): """Abstracts out the process of calling a method of a xenapi plugin. @@ -416,7 +418,7 @@ class VMOps(object): keys = [key_or_keys] else: keys = key_or_keys - keys.sort(lambda x,y: cmp(y.count('/'), x.count('/'))) + keys.sort(lambda x, y: cmp(y.count('/'), x.count('/'))) for key in keys: if path: keypath = "%s/%s" % (path, key) @@ -424,9 +426,8 @@ class VMOps(object): keypath = key self._make_xenstore_call('delete_record', vm, keypath) - ######################################################################## - ###### The following methods interact with the xenstore parameter + ###### The following methods interact with the xenstore parameter ###### record, not the live xenstore. They were created before I ###### knew the difference, and are left in here in case they prove ###### to be useful. They all have '_param' added to their method @@ -444,13 +445,14 @@ class VMOps(object): return data def read_from_param_xenstore(self, instance_or_vm, keys=None): - """Returns the xenstore parameter record data for the specified VM instance - as a dict. Accepts an optional key or list of keys; if a value for 'keys' - is passed, the returned dict is filtered to only return the values - for those keys. + """Returns the xenstore parameter record data for the specified VM + instance as a dict. Accepts an optional key or list of keys; if a + value for 'keys' is passed, the returned dict is filtered to only + return the values for those keys. """ vm = self._get_vm_opaque_ref(instance_or_vm) - data = self._session.call_xenapi_request('VM.get_xenstore_data', (vm, )) + data = self._session.call_xenapi_request('VM.get_xenstore_data', + (vm, )) ret = {} if keys is None: keys = data.keys() @@ -493,7 +495,8 @@ class VMOps(object): else: keys = key_or_keys for key in keys: - self._session.call_xenapi_request('VM.remove_from_xenstore_data', (vm, key)) + self._session.call_xenapi_request('VM.remove_from_xenstore_data', + (vm, key)) def clear_param_xenstore(self, instance_or_vm): """Removes all data from the xenstore parameter record for this VM.""" @@ -503,7 +506,9 @@ class VMOps(object): def _runproc(cmd): pipe = subprocess.PIPE - return subprocess.Popen([cmd], shell=True, stdin=pipe, stdout=pipe, stderr=pipe, close_fds=True) + return subprocess.Popen([cmd], shell=True, stdin=pipe, stdout=pipe, + stderr=pipe, close_fds=True) + class SimpleDH(object): """This class wraps all the functionality needed to implement @@ -514,23 +519,12 @@ class SimpleDH(object): as it uses that to handle the encryption and decryption. If openssl is not available, a RuntimeError will be raised. """ -# def __init__(self, prime=None, base=None): -# """You can specify the values for prime and base if you wish; -# otherwise, reasonable default values will be used. -# """ -# if prime is None: -# self._prime = 162259276829213363391578010288127 -# else: -# self._prime = prime -# if base is None: -# self._base = 5 -# else: -# self._base = base -# self._secret = random.randint(5000, 15000) -# self._shared = self._public = None def __init__(self, prime=None, base=None, secret=None): """You can specify the values for prime and base if you wish; - otherwise, reasonable default values will be used. + otherwise, reasonable default values will be used. You may also + specify the integer value for 'secret', but this should only be + done while testing when you need reproducible values. Otherwise, + any security benefits are lost. """ if prime is None: self._prime = 162259276829213363391578010288127 diff --git a/nova/virt/xenapi_conn.py b/nova/virt/xenapi_conn.py index fe302c450..3baae6188 100644 --- a/nova/virt/xenapi_conn.py +++ b/nova/virt/xenapi_conn.py @@ -275,16 +275,16 @@ class XenAPISession(object): error_info)) done.send_exception(self.XenAPI.Failure(error_info)) db.instance_action_create(context.get_admin_context(), action) -# import sqlalchemy -# from sqlalchemy.exc import IntegrityError as IntegrityError -# try: -# db.instance_action_create(context.get_admin_context(), action) -# except IntegrityError: -# # Some methods don't pass unique IDs, so the call to -# # instance_action_create() will raise IntegrityErrors. Rather -# # than bomb out, I'm explicitly silencing them so that the -# # code can continue to work until they fix that method. -# pass +# import sqlalchemy +# from sqlalchemy.exc import IntegrityError as IntegrityError +# try: +# db.instance_action_create(context.get_admin_context(), action) +# except IntegrityError: +# # Some methods don't pass unique IDs, so the call to +# # instance_action_create() will raise IntegrityErrors. Rather +# # than bomb out, I'm explicitly silencing them so that the +# # code can continue to work until they fix that method. +# pass except self.XenAPI.Failure, exc: logging.warn(exc) -- cgit From 9c17d68bb670f389a16e05d4306ad0a720e7a1e4 Mon Sep 17 00:00:00 2001 From: MORITA Kazutaka Date: Sat, 1 Jan 2011 02:53:45 +0900 Subject: Add support for various block device types (block, network, file) --- nova/virt/libvirt_conn.py | 25 ++++++++++++++++++++----- 1 file changed, 20 insertions(+), 5 deletions(-) (limited to 'nova') diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py index 00edfbdc8..51c805c2f 100644 --- a/nova/virt/libvirt_conn.py +++ b/nova/virt/libvirt_conn.py @@ -223,11 +223,26 @@ class LibvirtConnection(object): def attach_volume(self, instance_name, device_path, mountpoint): virt_dom = self._conn.lookupByName(instance_name) mount_device = mountpoint.rpartition("/")[2] - xml = """ - - - - """ % (device_path, mount_device) + xml = '' + (protocol, vol_name) = device_path.split(':') + if device_path.startswith('/dev/'): + xml = """ + + + + """ % (device_path, mount_device) + elif vol_name != '': + xml = """ + + + + """ % (protocol, vol_name, mount_device) + else: + xml = """ + + + + """ % (device_path, mount_device) virt_dom.attachDevice(xml) def _get_disk_xml(self, xml, device): -- cgit From f8272fc0b38be55d383860b6138e79a8a74965be Mon Sep 17 00:00:00 2001 From: MORITA Kazutaka Date: Sat, 1 Jan 2011 02:53:51 +0900 Subject: Add support for Sheepdog volumes --- nova/volume/driver.py | 49 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) (limited to 'nova') diff --git a/nova/volume/driver.py b/nova/volume/driver.py index 8353b9712..2e8a3c816 100644 --- a/nova/volume/driver.py +++ b/nova/volume/driver.py @@ -312,3 +312,52 @@ class FakeISCSIDriver(ISCSIDriver): """Execute that simply logs the command.""" logging.debug(_("FAKE ISCSI: %s"), cmd) return (None, None) + + +class SheepdogDriver(VolumeDriver): + """Executes commands relating to Sheepdog Volumes""" + + def check_for_setup_error(self): + """Returns an error if prerequisites aren't met""" + try: + (out, err) = self._execute("collie cluster info") + if not out.startswith('running'): + raise exception.Error(_("Sheepdog is not working: %s") % out) + except exception.ProcessExecutionError: + raise exception.Error(_("Sheepdog is not working")) + + def create_volume(self, volume): + """Creates a sheepdog volume""" + if int(volume['size']) == 0: + sizestr = '100M' + else: + sizestr = '%sG' % volume['size'] + self._try_execute("qemu-img create sheepdog:%s %s" % + (volume['name'], sizestr)) + + def delete_volume(self, volume): + """Deletes a logical volume""" + self._try_execute("collie vdi delete %s" % volume['name']) + + def local_path(self, volume): + return "sheepdog:%s" % volume['name'] + + def ensure_export(self, context, volume): + """Safely and synchronously recreates an export for a logical volume""" + pass + + def create_export(self, context, volume): + """Exports the volume""" + pass + + def remove_export(self, context, volume): + """Removes an export for a logical volume""" + pass + + def discover_volume(self, volume): + """Discover volume on a remote host""" + return "sheepdog:%s" % volume['name'] + + def undiscover_volume(self, volume): + """Undiscover volume on a remote host""" + pass -- cgit From 108352d5c132f6accc79974d8c646a2bc7d4f127 Mon Sep 17 00:00:00 2001 From: Ed Leafe Date: Fri, 31 Dec 2010 12:21:04 -0600 Subject: Updated the password generation code --- nova/compute/manager.py | 8 +++++--- nova/virt/fake.py | 2 +- nova/virt/xenapi/vmops.py | 4 ++-- 3 files changed, 8 insertions(+), 6 deletions(-) (limited to 'nova') diff --git a/nova/compute/manager.py b/nova/compute/manager.py index 8ebd87f28..583fabe42 100644 --- a/nova/compute/manager.py +++ b/nova/compute/manager.py @@ -36,6 +36,7 @@ terminating it. import datetime import logging +import random import string from nova import exception @@ -247,7 +248,7 @@ class ComputeManager(manager.Manager): self.driver.snapshot(instance_ref, name) @exception.wrap_exception - def reset_root_password(self, context, instance_id): + def reset_root_password(self, context, instance_id, new_pass=None): """Reset the root/admin password for an instance on this server.""" context = context.elevated() instance_ref = self.db.instance_get(context, instance_id) @@ -264,8 +265,9 @@ class ComputeManager(manager.Manager): instance_ref['name']) self.db.instance_set_state(context, instance_id, power_state.NOSTATE, 'resetting_password') - #### TODO: (dabo) not sure how we will implement this yet. - new_pass = self._generate_password(12) + if new_pass is None: + # Generate a random, 12-character password + new_pass = self._generate_password(12) self.driver.reset_root_password(instance_ref, new_pass) self._update_state(context, instance_id) diff --git a/nova/virt/fake.py b/nova/virt/fake.py index 13c67c4ee..fdc8ac5fb 100644 --- a/nova/virt/fake.py +++ b/nova/virt/fake.py @@ -138,7 +138,7 @@ class FakeConnection(object): """ pass - def reset_root_password(self, instance): + def reset_root_password(self, instance, new_pass): """ Reset the root password on the specified instance. diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py index 49857e3d9..4ce8d819b 100644 --- a/nova/virt/xenapi/vmops.py +++ b/nova/virt/xenapi/vmops.py @@ -61,8 +61,8 @@ class VMOps(object): """Create VM instance""" vm = VMHelper.lookup(self._session, instance.name) if vm is not None: - msg = _('Attempted to create non-unique name %s') % instance.name) - raise exception.Duplicate( + msg = _('Attempted to create non-unique name %s') % instance.name + raise exception.Duplicate(msg) bridge = db.network_get_by_instance(context.get_admin_context(), instance['id'])['bridge'] -- cgit From 56969837fdf1a9e5316443ce72b32ae268ed2947 Mon Sep 17 00:00:00 2001 From: Nachi Ueno Date: Tue, 4 Jan 2011 04:21:50 -0500 Subject: Fixed conflict with r515 --- nova/virt/libvirt.uml.xml.template | 27 --------------------------- 1 file changed, 27 deletions(-) delete mode 100644 nova/virt/libvirt.uml.xml.template (limited to 'nova') diff --git a/nova/virt/libvirt.uml.xml.template b/nova/virt/libvirt.uml.xml.template deleted file mode 100644 index 0d355b81c..000000000 --- a/nova/virt/libvirt.uml.xml.template +++ /dev/null @@ -1,27 +0,0 @@ - - %(name)s - %(memory_kb)s - - %(type)s - /usr/bin/linux - /dev/ubda1 - - - - - - - - - - - - - - - - - - - - -- cgit From 96facdeee025cdf33df0b16abeeeb97f9ec87e70 Mon Sep 17 00:00:00 2001 From: Nachi Ueno Date: Tue, 4 Jan 2011 04:27:39 -0500 Subject: Fixed for pep8 --- nova/virt/libvirt_conn.py | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) (limited to 'nova') diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py index 9a99b1a51..c656931d6 100644 --- a/nova/virt/libvirt_conn.py +++ b/nova/virt/libvirt_conn.py @@ -550,7 +550,7 @@ class LibvirtConnection(object): "\n" "\n") % (net, mask,net_v6,mask_v6) + "value=\"%s\" />\n") % (net, mask, net_v6, mask_v6) else: extra_params = "\n" @@ -777,7 +777,6 @@ class NWFilterFirewall(object): ''' - nova_ra_filter = ''' d707fa71-4fb5-4b27-9ab7-ba5ca19c8804 ''' - nova_vpn_filter = ''' 2086015e-cf03-11df-8c5d-080027c27973 @@ -795,7 +793,6 @@ class NWFilterFirewall(object): ''' - def nova_base_ipv4_filter(self): retval = "" for protocol in ['tcp', 'udp', 'icmp']: @@ -832,8 +829,9 @@ class NWFilterFirewall(object): def nova_project_filter_v6(self): retval = "" % project for protocol in ['tcp-ipv6', 'udp-ipv6', 'icmpv6']: - retval += """ - <%s srcipaddr='$PROJNETV6' srcipmask='$PROJMASKV6' /> + retval += """ + <%s srcipaddr='$PROJNETV6' + srcipmask='$PROJMASKV6' /> """ % (protocol) retval += '' return retval @@ -872,7 +870,7 @@ class NWFilterFirewall(object): if FLAGS.allow_project_net_traffic: nwfilter_xml += " \n" if(FLAGS.use_ipv6): - nwfilter_xml += " \n" + nwfilter_xml += " \n" for security_group in instance.security_groups: self.ensure_security_group_filter(security_group['id']) @@ -892,7 +890,7 @@ class NWFilterFirewall(object): security_group_id) rule_xml = "" version = 4 - v6protocol = {'tcp':'tcp-ipv6', 'udp':'udp-ipv6', 'icmp':'icmpv6'} + v6protocol = {'tcp': 'tcp-ipv6', 'udp': 'udp-ipv6', 'icmp': 'icmpv6'} for rule in security_group.rules: rule_xml += "" if rule.cidr: @@ -904,7 +902,6 @@ class NWFilterFirewall(object): else: rule_xml += "<%s srcipaddr='%s' srcipmask='%s' " % \ (rule.protocol, net, mask) - if rule.protocol in ['tcp', 'udp']: rule_xml += "dstportstart='%s' dstportend='%s' " % \ (rule.from_port, rule.to_port) -- cgit From 505becef0704cc801f957d2931c8b994e2df92ca Mon Sep 17 00:00:00 2001 From: nova Date: Tue, 4 Jan 2011 05:00:21 -0500 Subject: Fixed bug --- nova/virt/libvirt_conn.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'nova') diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py index c656931d6..de7f6341f 100644 --- a/nova/virt/libvirt_conn.py +++ b/nova/virt/libvirt_conn.py @@ -827,7 +827,7 @@ class NWFilterFirewall(object): return retval def nova_project_filter_v6(self): - retval = "" % project + retval = "" for protocol in ['tcp-ipv6', 'udp-ipv6', 'icmpv6']: retval += """ <%s srcipaddr='$PROJNETV6' -- cgit From c528be81a5d0acaea5077c183ec4d15356d457d5 Mon Sep 17 00:00:00 2001 From: Nachi Ueno Date: Tue, 4 Jan 2011 05:35:13 -0500 Subject: Fixed bug in libvirt --- nova/virt/libvirt_conn.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) (limited to 'nova') diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py index de7f6341f..f95544188 100644 --- a/nova/virt/libvirt_conn.py +++ b/nova/virt/libvirt_conn.py @@ -114,6 +114,9 @@ def _get_net_and_mask(cidr): net = IPy.IP(cidr) return str(net.net()), str(net.netmask()) +def _get_ip_version(cidr): + net = IPy.IP(cidr) + return int(net.version()) class LibvirtConnection(object): @@ -484,7 +487,8 @@ class LibvirtConnection(object): 'netmask': network_ref['netmask'], 'gateway': network_ref['gateway'], 'broadcast': network_ref['broadcast'], - 'dns': network_ref['dns']} + 'dns': network_ref['dns'], + 'ra_server': network_ref['ra_server']} if key or net: if key: logging.info(_('instance %s: injecting key into image %s'), @@ -541,8 +545,8 @@ class LibvirtConnection(object): if FLAGS.allow_project_net_traffic: net, mask = _get_net_and_mask(network['cidr']) - net_v6, mask_v6 = self._get_net_and_mask( - network_ref['cidr_v6']) + net_v6, mask_v6 = _get_net_and_mask( + network['cidr_v6']) extra_params = ("\n" " Date: Tue, 4 Jan 2011 07:40:29 -0500 Subject: Some Bug Fix --- nova/utils.py | 4 +--- nova/virt/libvirt.xml.template | 2 +- nova/virt/libvirt_conn.py | 16 +++++++++++----- 3 files changed, 13 insertions(+), 9 deletions(-) (limited to 'nova') diff --git a/nova/utils.py b/nova/utils.py index 0eab4b152..c5e2f7517 100644 --- a/nova/utils.py +++ b/nova/utils.py @@ -211,8 +211,6 @@ def get_my_ip(): def get_my_linklocal(interface): - if getattr(FLAGS, 'fake_tests', None): - return 'fe00::' try: if_str = execute("ifconfig %s" % interface) condition = "\s+inet6\s+addr:\s+([0-9a-f:]+/\d+)\s+Scope:Link" @@ -224,7 +222,7 @@ def get_my_linklocal(interface): return None except RuntimeError as ex: logging.warn("Couldn't get Link Local IP of %s :%s", interface, ex) - return None + return 'fe00::' def to_global_ipv6(prefix, mac): diff --git a/nova/virt/libvirt.xml.template b/nova/virt/libvirt.xml.template index f0f56fc62..52c95ee57 100644 --- a/nova/virt/libvirt.xml.template +++ b/nova/virt/libvirt.xml.template @@ -66,7 +66,7 @@ - + #if $getVar('extra_params', False) ${extra_params} #end if diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py index f95544188..521ac97fc 100644 --- a/nova/virt/libvirt_conn.py +++ b/nova/virt/libvirt_conn.py @@ -114,6 +114,10 @@ def _get_net_and_mask(cidr): net = IPy.IP(cidr) return str(net.net()), str(net.netmask()) +def _get_net_and_prefixlen(cidr): + net = IPy.IP(cidr) + return str(net.net()), str(net.prefixlen()) + def _get_ip_version(cidr): net = IPy.IP(cidr) return int(net.version()) @@ -354,6 +358,7 @@ class LibvirtConnection(object): power_state.NOSTATE, 'launching') NWFilterFirewall(self._conn).setup_nwfilters_for_instance(instance) + self._create_image(instance, xml) self._conn.createXML(xml, 0) logging.debug(_("instance %s: is running"), instance['name']) @@ -545,7 +550,7 @@ class LibvirtConnection(object): if FLAGS.allow_project_net_traffic: net, mask = _get_net_and_mask(network['cidr']) - net_v6, mask_v6 = _get_net_and_mask( + net_v6, prefixlen_v6 = _get_net_and_prefixlen( network['cidr_v6']) extra_params = ("\n" @@ -554,7 +559,7 @@ class LibvirtConnection(object): "\n" "\n") % (net, mask, net_v6, mask_v6) + "value=\"%s\" />\n") % (net, mask, net_v6, prefixlen_v6) else: extra_params = "\n" @@ -882,7 +887,7 @@ class NWFilterFirewall(object): nwfilter_xml += (" \n") % security_group['id'] nwfilter_xml += "" - + logging.debug(nwfilter_xml) self._define_filter(nwfilter_xml) def ensure_security_group_filter(self, security_group_id): @@ -899,11 +904,12 @@ class NWFilterFirewall(object): rule_xml += "" if rule.cidr: version = _get_ip_version(rule.cidr) - net, mask = _get_net_and_mask(rule.cidr) if(FLAGS.use_ipv6 and version == 6): + net, prefixlen = _get_net_and_prefixlen(rule.cidr) rule_xml += "<%s srcipaddr='%s' srcipmask='%s' " % \ - (v6protocol[rrule.protocol], net, mask) + (v6protocol[rrule.protocol], net, prefixlen) else: + net, mask = _get_net_and_mask(rule.cidr) rule_xml += "<%s srcipaddr='%s' srcipmask='%s' " % \ (rule.protocol, net, mask) if rule.protocol in ['tcp', 'udp']: -- cgit From 28bf4e2df324db79a81a853d39cb5912985c2e45 Mon Sep 17 00:00:00 2001 From: Nachi Ueno Date: Wed, 5 Jan 2011 10:25:16 -0500 Subject: Fixed bug in nova_project_filter_v6 --- nova/virt/libvirt_conn.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) (limited to 'nova') diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py index 521ac97fc..a592f5d6b 100644 --- a/nova/virt/libvirt_conn.py +++ b/nova/virt/libvirt_conn.py @@ -114,14 +114,17 @@ def _get_net_and_mask(cidr): net = IPy.IP(cidr) return str(net.net()), str(net.netmask()) + def _get_net_and_prefixlen(cidr): net = IPy.IP(cidr) return str(net.net()), str(net.prefixlen()) + def _get_ip_version(cidr): net = IPy.IP(cidr) return int(net.version()) + class LibvirtConnection(object): def __init__(self, read_only): @@ -559,7 +562,8 @@ class LibvirtConnection(object): "\n" "\n") % (net, mask, net_v6, prefixlen_v6) + "value=\"%s\" />\n") % \ + (net, mask, net_v6, prefixlen_v6) else: extra_params = "\n" @@ -838,7 +842,8 @@ class NWFilterFirewall(object): def nova_project_filter_v6(self): retval = "" for protocol in ['tcp-ipv6', 'udp-ipv6', 'icmpv6']: - retval += """ + retval += """ <%s srcipaddr='$PROJNETV6' srcipmask='$PROJMASKV6' /> """ % (protocol) -- cgit From b47f37d0f9c06f2c4bc5adcf3afcececa2354324 Mon Sep 17 00:00:00 2001 From: Nachi Ueno Date: Wed, 5 Jan 2011 10:35:15 -0500 Subject: Fixed misspelled variable --- nova/virt/libvirt_conn.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'nova') diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py index a592f5d6b..1f3c69f65 100644 --- a/nova/virt/libvirt_conn.py +++ b/nova/virt/libvirt_conn.py @@ -912,7 +912,7 @@ class NWFilterFirewall(object): if(FLAGS.use_ipv6 and version == 6): net, prefixlen = _get_net_and_prefixlen(rule.cidr) rule_xml += "<%s srcipaddr='%s' srcipmask='%s' " % \ - (v6protocol[rrule.protocol], net, prefixlen) + (v6protocol[rule.protocol], net, prefixlen) else: net, mask = _get_net_and_mask(rule.cidr) rule_xml += "<%s srcipaddr='%s' srcipmask='%s' " % \ -- cgit From 69b7a0d69c3ac79b84c2bda19d379606c5a323ab Mon Sep 17 00:00:00 2001 From: Nachi Ueno Date: Wed, 5 Jan 2011 10:42:15 -0500 Subject: Removed debug message which is not needed. --- nova/virt/libvirt_conn.py | 1 - 1 file changed, 1 deletion(-) (limited to 'nova') diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py index 1f3c69f65..4fba164a9 100644 --- a/nova/virt/libvirt_conn.py +++ b/nova/virt/libvirt_conn.py @@ -892,7 +892,6 @@ class NWFilterFirewall(object): nwfilter_xml += (" \n") % security_group['id'] nwfilter_xml += "" - logging.debug(nwfilter_xml) self._define_filter(nwfilter_xml) def ensure_security_group_filter(self, security_group_id): -- cgit From 40b156f74e90a94abb255950f29d714f4bc4c428 Mon Sep 17 00:00:00 2001 From: Nachi Ueno Date: Wed, 5 Jan 2011 12:36:47 -0500 Subject: Fixed:Create instance fails when use_ipv6=False --- nova/virt/libvirt_conn.py | 28 +++++++++++++++++++++------- 1 file changed, 21 insertions(+), 7 deletions(-) (limited to 'nova') diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py index 4fba164a9..8197342df 100644 --- a/nova/virt/libvirt_conn.py +++ b/nova/virt/libvirt_conn.py @@ -490,13 +490,16 @@ class LibvirtConnection(object): if network_ref['injected']: admin_context = context.get_admin_context() address = db.instance_get_fixed_address(admin_context, inst['id']) + ra_server = network_ref['ra_server'] + if not ra_server: + ra_server = "fd00::" with open(FLAGS.injected_network_template) as f: net = f.read() % {'address': address, 'netmask': network_ref['netmask'], 'gateway': network_ref['gateway'], 'broadcast': network_ref['broadcast'], 'dns': network_ref['dns'], - 'ra_server': network_ref['ra_server']} + 'ra_server': ra_server} if key or net: if key: logging.info(_('instance %s: injecting key into image %s'), @@ -550,12 +553,14 @@ class LibvirtConnection(object): # Assume that the gateway also acts as the dhcp server. dhcp_server = network['gateway'] ra_server = network['ra_server'] - + if not ra_server: + ra_server = 'fd00::' if FLAGS.allow_project_net_traffic: - net, mask = _get_net_and_mask(network['cidr']) - net_v6, prefixlen_v6 = _get_net_and_prefixlen( + if FLAGS.use_ipv6: + net, mask = _get_net_and_mask(network['cidr']) + net_v6, prefixlen_v6 = _get_net_and_prefixlen( network['cidr_v6']) - extra_params = ("\n" "\n" @@ -564,6 +569,13 @@ class LibvirtConnection(object): "\n") % \ (net, mask, net_v6, prefixlen_v6) + else: + net, mask = _get_net_and_mask(network['cidr']) + extra_params = ("\n" + "\n") % \ + (net, mask) else: extra_params = "\n" @@ -860,12 +872,14 @@ class NWFilterFirewall(object): self._define_filter(self.nova_base_ipv4_filter) self._define_filter(self.nova_base_ipv6_filter) self._define_filter(self.nova_dhcp_filter) - self._define_filter(self.nova_ra_filter) + if FLAGS.use_ipv6: + self._define_filter(self.nova_ra_filter) self._define_filter(self.nova_base_filter) self._define_filter(self.nova_vpn_filter) if FLAGS.allow_project_net_traffic: self._define_filter(self.nova_project_filter) - self._define_filter(self.nova_project_filter_v6) + if FLAGS.use_ipv6: + self._define_filter(self.nova_project_filter_v6) def setup_nwfilters_for_instance(self, instance): """ -- cgit From 90ced5f211c3a53389d2f5d7413f9289770b279a Mon Sep 17 00:00:00 2001 From: Nachi Ueno Date: Wed, 5 Jan 2011 12:38:34 -0500 Subject: Fixed for pep8 --- nova/virt/libvirt_conn.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'nova') diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py index 8197342df..b19988822 100644 --- a/nova/virt/libvirt_conn.py +++ b/nova/virt/libvirt_conn.py @@ -490,9 +490,9 @@ class LibvirtConnection(object): if network_ref['injected']: admin_context = context.get_admin_context() address = db.instance_get_fixed_address(admin_context, inst['id']) - ra_server = network_ref['ra_server'] + ra_server = network_ref['ra_server'] if not ra_server: - ra_server = "fd00::" + ra_server = "fd00::" with open(FLAGS.injected_network_template) as f: net = f.read() % {'address': address, 'netmask': network_ref['netmask'], @@ -554,7 +554,7 @@ class LibvirtConnection(object): dhcp_server = network['gateway'] ra_server = network['ra_server'] if not ra_server: - ra_server = 'fd00::' + ra_server = 'fd00::' if FLAGS.allow_project_net_traffic: if FLAGS.use_ipv6: net, mask = _get_net_and_mask(network['cidr']) -- cgit From f85eba86b04253612e2272b3eb6a9fd79fab6567 Mon Sep 17 00:00:00 2001 From: Nachi Ueno Date: Wed, 5 Jan 2011 12:39:35 -0500 Subject: missing _() --- nova/utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'nova') diff --git a/nova/utils.py b/nova/utils.py index c5e2f7517..afe7422d9 100644 --- a/nova/utils.py +++ b/nova/utils.py @@ -221,7 +221,7 @@ def get_my_linklocal(interface): else: return None except RuntimeError as ex: - logging.warn("Couldn't get Link Local IP of %s :%s", interface, ex) + logging.warn(_("Couldn't get Link Local IP of %s :%s"), interface, ex) return 'fe00::' -- cgit From f67802d62ee530b4e81aaf108dfd3813c84550b2 Mon Sep 17 00:00:00 2001 From: Ed Leafe Date: Wed, 5 Jan 2011 16:41:50 -0600 Subject: intermediate work --- nova/api/__init__.py | 14 ++++++++++++-- nova/api/openstack/servers.py | 7 ++++--- nova/compute/api.py | 2 +- nova/virt/xenapi/vmops.py | 19 ++++++++++++++----- 4 files changed, 31 insertions(+), 11 deletions(-) (limited to 'nova') diff --git a/nova/api/__init__.py b/nova/api/__init__.py index 26fed847b..ff936bed2 100644 --- a/nova/api/__init__.py +++ b/nova/api/__init__.py @@ -59,13 +59,23 @@ class API(wsgi.Router): mapper.connect("/", controller=self.osapi_versions, conditions=osapi_subdomain) - mapper.connect("/v1.0/{path_info:.*}", controller=openstack.API(), - conditions=osapi_subdomain) + mapper.connect("/v1.0/{path_info:.*}", controller=openstack.API()) mapper.connect("/", controller=self.ec2api_versions, conditions=ec2api_subdomain) mapper.connect("/services/{path_info:.*}", controller=ec2.API(), conditions=ec2api_subdomain) + +# mapper.connect("/", controller=self.osapi_versions, +# conditions=osapi_subdomain) +# mapper.connect("/v1.0/{path_info:.*}", controller=openstack.API(), +# conditions=osapi_subdomain) +# +# mapper.connect("/", controller=self.ec2api_versions, +# conditions=ec2api_subdomain) +# mapper.connect("/services/{path_info:.*}", controller=ec2.API(), +# conditions=ec2api_subdomain) + mrh = metadatarequesthandler.MetadataRequestHandler() for s in ['/latest', '/2009-04-04', diff --git a/nova/api/openstack/servers.py b/nova/api/openstack/servers.py index c5cbe21ef..4bdb14d04 100644 --- a/nova/api/openstack/servers.py +++ b/nova/api/openstack/servers.py @@ -152,9 +152,10 @@ class Controller(wsgi.Controller): try: ctxt = req.environ['nova.context'] - self.compute_api.update_instance(ctxt, - id, - **update_dict) + # The ID passed in is actually the internal_id of the + # instance, not the value of the id column in the DB. + instance = self.compute_api.get_instance(ctxt, id) + self.compute_api.update_instance(ctxt, instance.id, **update_dict) except exception.NotFound: return faults.Fault(exc.HTTPNotFound()) return exc.HTTPNoContent() diff --git a/nova/compute/api.py b/nova/compute/api.py index 76a571d61..1d21a4668 100644 --- a/nova/compute/api.py +++ b/nova/compute/api.py @@ -257,7 +257,7 @@ class ComputeAPI(base.Base): def get_instance(self, context, instance_id): return self.db.instance_get_by_internal_id(context, instance_id) - def _cast_compute_message(method, context, instance_id): + def _cast_compute_message(self, method, context, instance_id): """Generic handler for RPC calls.""" instance = self.get_instance(context, instance_id) host = instance['host'] diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py index 78f2d4704..eaf8c7dff 100644 --- a/nova/virt/xenapi/vmops.py +++ b/nova/virt/xenapi/vmops.py @@ -130,12 +130,18 @@ class VMOps(object): """Refactored out the common code of many methods that receive either a vm name or a vm instance, and want a vm instance in return. """ + vm = None try: - instance_name = instance_or_vm.name - vm = VMHelper.lookup(self._session, instance_name) + if instance_or_vm.startswith("OpaqueRef:"): + # Got passed an opaque ref; return it + return instance_or_vm + else: + # Must be the instance name + instance_name = instance_or_vm except AttributeError: - # A vm opaque ref was passed - vm = instance_or_vm + # Not a string; must be a vm instance + instance_name = instance_or_vm.name + vm = VMHelper.lookup(self._session, instance_name) if vm is None: raise Exception(_('Instance not present %s') % instance_name) return vm @@ -201,6 +207,9 @@ class VMOps(object): instead of the more advanced one in M2Crypto for compatibility with the agent code. """ + + logging.error("ZZZZ RESET PASS CALLED") + # Need to uniquely identify this request. transaction_id = str(uuid.uuid4()) # The simple Diffie-Hellman class is used to manage key exchange. @@ -291,7 +300,7 @@ class VMOps(object): task = self._session.call_xenapi('Async.VM.resume', vm, False, True) self._wait_with_callback(task, callback) - def get_info(self, instance_id): + def get_info(self, instance): """Return data about VM instance""" vm = self._get_vm_opaque_ref(instance) rec = self._session.get_xenapi().VM.get_record(vm) -- cgit From a3e12f5eb92921acc622ea7bd9097edeea0d40fd Mon Sep 17 00:00:00 2001 From: Ed Leafe Date: Thu, 6 Jan 2011 06:45:14 -0600 Subject: Renamed 'set_root_password' to 'set_admin_password' globally. --- nova/compute/api.py | 50 ++++++++++++++++------------------------------ nova/compute/manager.py | 24 ++++++++++++---------- nova/tests/test_compute.py | 6 +++--- nova/virt/fake.py | 19 +++++++++--------- nova/virt/xenapi/vmops.py | 20 +++++++++---------- nova/virt/xenapi_conn.py | 6 +++--- 6 files changed, 56 insertions(+), 69 deletions(-) (limited to 'nova') diff --git a/nova/compute/api.py b/nova/compute/api.py index 1d21a4668..0a7b802d6 100644 --- a/nova/compute/api.py +++ b/nova/compute/api.py @@ -190,7 +190,7 @@ class ComputeAPI(base.Base): """ try: db.security_group_get_by_name(context, context.project_id, - 'default') + 'default') except exception.NotFound: values = {'name': 'default', 'description': 'default', @@ -258,70 +258,54 @@ class ComputeAPI(base.Base): return self.db.instance_get_by_internal_id(context, instance_id) def _cast_compute_message(self, method, context, instance_id): - """Generic handler for RPC calls.""" + """Generic handler for RPC calls to compute.""" instance = self.get_instance(context, instance_id) host = instance['host'] rpc.cast(context, - self.db.queue_get_for(context, FLAGS.compute_topic, host), - {"method": method, - "args": {"instance_id": instance['id']}}) + self.db.queue_get_for(context, FLAGS.compute_topic, host), + {'method': method, 'args': {'instance_id': instance['id']}}) def snapshot(self, context, instance_id, name): """Snapshot the given instance.""" - self._cast_compute_message("snapshot_instance", context, instance_id) + self._cast_compute_message('snapshot_instance', context, instance_id) def reboot(self, context, instance_id): """Reboot the given instance.""" - self._cast_compute_message("reboot_instance", context, instance_id) + self._cast_compute_message('reboot_instance', context, instance_id) def pause(self, context, instance_id): """Pause the given instance.""" - self._cast_compute_message("pause_instance", context, instance_id) + self._cast_compute_message('pause_instance', context, instance_id) def unpause(self, context, instance_id): """Unpause the given instance.""" - self._cast_compute_message("unpause_instance", context, instance_id) + self._cast_compute_message('unpause_instance', context, instance_id) def get_diagnostics(self, context, instance_id): """Retrieve diagnostics for the given instance.""" - instance = self.db.instance_get_by_internal_id(context, instance_id) - host = instance["host"] - return rpc.call(context, - self.db.queue_get_for(context, FLAGS.compute_topic, host), - {"method": "get_diagnostics", - "args": {"instance_id": instance["id"]}}) + self._cast_compute_message('get_diagnostics', context, instance_id) def get_actions(self, context, instance_id): """Retrieve actions for the given instance.""" instance = self.db.instance_get_by_internal_id(context, instance_id) - return self.db.instance_get_actions(context, instance["id"]) + return self.db.instance_get_actions(context, instance['id']) def suspend(self, context, instance_id): """suspend the instance with instance_id""" - instance = self.db.instance_get_by_internal_id(context, instance_id) - host = instance['host'] - rpc.cast(context, - self.db.queue_get_for(context, FLAGS.compute_topic, host), - {"method": "suspend_instance", - "args": {"instance_id": instance['id']}}) + self._cast_compute_message('suspend_instance', context, instance_id) def resume(self, context, instance_id): """resume the instance with instance_id""" - instance = self.db.instance_get_by_internal_id(context, instance_id) - host = instance['host'] - rpc.cast(context, - self.db.queue_get_for(context, FLAGS.compute_topic, host), - {"method": "resume_instance", - "args": {"instance_id": instance['id']}}) + self._cast_compute_message('resume_instance', context, instance_id) def rescue(self, context, instance_id): """Rescue the given instance.""" - self._cast_compute_message("rescue_instance", context, instance_id) + self._cast_compute_message('rescue_instance', context, instance_id) def unrescue(self, context, instance_id): """Unrescue the given instance.""" - self._cast_compute_message("unrescue_instance", context, instance_id) + self._cast_compute_message('unrescue_instance', context, instance_id) - def reset_root_password(self, context, instance_id): - """Reset the root/admin pw for the given instance.""" - self._cast_compute_message("reset_root_password", context, instance_id) + def set_admin_password(self, context, instance_id): + """Set the root/admin password for the given instance.""" + self._cast_compute_message('set_admin_password', context, instance_id) diff --git a/nova/compute/manager.py b/nova/compute/manager.py index 6f4d14589..b8bf91530 100644 --- a/nova/compute/manager.py +++ b/nova/compute/manager.py @@ -53,6 +53,8 @@ flags.DEFINE_string('compute_driver', 'nova.virt.connection.get_connection', 'Driver to use for controlling virtualization') flags.DEFINE_string('stub_network', False, 'Stub network related code') +flags.DEFINE_integer('password_length', 12, + 'Length of generated admin passwords') class ComputeManager(manager.Manager): @@ -248,27 +250,27 @@ class ComputeManager(manager.Manager): self.driver.snapshot(instance_ref, name) @exception.wrap_exception - def reset_root_password(self, context, instance_id, new_pass=None): - """Reset the root/admin password for an instance on this server.""" + def set_admin_password(self, context, instance_id, new_pass=None): + """Set the root/admin password for an instance on this server.""" context = context.elevated() instance_ref = self.db.instance_get(context, instance_id) self._update_state(context, instance_id) if instance_ref['state'] != power_state.RUNNING: logging.warn('trying to reset the password on a non-running ' - 'instance: %s (state: %s excepted: %s)', - instance_ref['internal_id'], - instance_ref['state'], - power_state.RUNNING) + 'instance: %s (state: %s expected: %s)', + instance_ref['internal_id'], + instance_ref['state'], + power_state.RUNNING) - logging.debug('instance %s: resetting root password', + logging.debug('instance %s: setting admin password', instance_ref['name']) self.db.instance_set_state(context, instance_id, - power_state.NOSTATE, 'resetting_password') + power_state.NOSTATE, 'setting_password') if new_pass is None: - # Generate a random, 12-character password - new_pass = self._generate_password(12) - self.driver.reset_root_password(instance_ref, new_pass) + # Generate a random password + new_pass = self._generate_password(FLAGS.password_length) + self.driver.set_admin_password(instance_ref, new_pass) self._update_state(context, instance_id) def _generate_password(self, length=20): diff --git a/nova/tests/test_compute.py b/nova/tests/test_compute.py index 7c258e636..88e14d7df 100644 --- a/nova/tests/test_compute.py +++ b/nova/tests/test_compute.py @@ -151,11 +151,11 @@ class ComputeTestCase(test.TestCase): self.compute.reboot_instance(self.context, instance_id) self.compute.terminate_instance(self.context, instance_id) - def test_reset_root_password(self): - """Ensure instance can have its root password reset""" + def test_set_admin_password(self): + """Ensure instance can have its admin password set""" instance_id = self._create_instance() self.compute.run_instance(self.context, instance_id) - self.compute.reset_root_password(self.context, instance_id) + self.compute.set_admin_password(self.context, instance_id) self.compute.terminate_instance(self.context, instance_id) def test_snapshot(self): diff --git a/nova/virt/fake.py b/nova/virt/fake.py index 2b9cf1ca3..2d4b0a3d7 100644 --- a/nova/virt/fake.py +++ b/nova/virt/fake.py @@ -98,7 +98,7 @@ class FakeConnection(object): the new instance. The work will be done asynchronously. This function returns a - Deferred that allows the caller to detect when it is complete. + task that allows the caller to detect when it is complete. Once this successfully completes, the instance should be running (power_state.RUNNING). @@ -122,7 +122,7 @@ class FakeConnection(object): The second parameter is the name of the snapshot. The work will be done asynchronously. This function returns a - Deferred that allows the caller to detect when it is complete. + task that allows the caller to detect when it is complete. """ pass @@ -134,19 +134,20 @@ class FakeConnection(object): and so the instance is being specified as instance.name. The work will be done asynchronously. This function returns a - Deferred that allows the caller to detect when it is complete. + task that allows the caller to detect when it is complete. """ pass - def reset_root_password(self, instance, new_pass): + def set_admin_password(self, instance, new_pass): """ - Reset the root password on the specified instance. + Set the root password on the specified instance. - The given parameter is an instance of nova.compute.service.Instance, - and so the instance is being specified as instance.name. + The first parameter is an instance of nova.compute.service.Instance, + and so the instance is being specified as instance.name. The second + parameter is the value of the new password. The work will be done asynchronously. This function returns a - Deferred that allows the caller to detect when it is complete. + task that allows the caller to detect when it is complete. """ pass @@ -194,7 +195,7 @@ class FakeConnection(object): and so the instance is being specified as instance.name. The work will be done asynchronously. This function returns a - Deferred that allows the caller to detect when it is complete. + task that allows the caller to detect when it is complete. """ del self.instances[instance.name] diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py index eaf8c7dff..64855001b 100644 --- a/nova/virt/xenapi/vmops.py +++ b/nova/virt/xenapi/vmops.py @@ -198,17 +198,16 @@ class VMOps(object): task = self._session.call_xenapi('Async.VM.clean_reboot', vm) self._session.wait_for_task(instance.id, task) - def reset_root_password(self, instance, new_pass): - """Reset the root/admin password on the VM instance. This is - done via an agent running on the VM. Communication between - nova and the agent is done via writing xenstore records. Since - communication is done over the XenAPI RPC calls, we need to - encrypt the password. We're using a simple Diffie-Hellman class - instead of the more advanced one in M2Crypto for compatibility - with the agent code. + def set_admin_password(self, instance, new_pass): + """Set the root/admin password on the VM instance. This is done via + an agent running on the VM. Communication between nova and the agent + is done via writing xenstore records. Since communication is done over + the XenAPI RPC calls, we need to encrypt the password. We're using a + simple Diffie-Hellman class instead of the more advanced one in + M2Crypto for compatibility with the agent code. """ - logging.error("ZZZZ RESET PASS CALLED") + logging.error("ZZZZ SET PASS CALLED") # Need to uniquely identify this request. transaction_id = str(uuid.uuid4()) @@ -219,7 +218,8 @@ class VMOps(object): resp_dict = json.loads(resp) # Successful return code from key_init is 'D0' if resp_dict['returncode'] != 'D0': - # There was some sort of error + # There was some sort of error; the message will contain + # a description of the error. raise RuntimeError(resp_dict['message']) agent_pub = int(resp_dict['message']) dh.compute_shared(agent_pub) diff --git a/nova/virt/xenapi_conn.py b/nova/virt/xenapi_conn.py index f4dd7055c..0cba2812d 100644 --- a/nova/virt/xenapi_conn.py +++ b/nova/virt/xenapi_conn.py @@ -144,9 +144,9 @@ class XenAPIConnection(object): """Reboot VM instance""" self._vmops.reboot(instance) - def reset_root_password(self, instance, new_pass): - """Reset the root/admin password on the VM instance""" - self._vmops.reset_root_password(instance, new_pass) + def set_admin_password(self, instance, new_pass): + """Set the root/admin password on the VM instance""" + self._vmops.set_admin_password(instance, new_pass) def destroy(self, instance): """Destroy VM instance""" -- cgit From 0209ad587b2d8d35a7abdf60ca9b33391cab4a83 Mon Sep 17 00:00:00 2001 From: Ed Leafe Date: Thu, 6 Jan 2011 07:21:11 -0600 Subject: merged trunk changes --- nova/compute/api.py | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) (limited to 'nova') diff --git a/nova/compute/api.py b/nova/compute/api.py index 476667fa8..19cdf2d0a 100644 --- a/nova/compute/api.py +++ b/nova/compute/api.py @@ -61,13 +61,11 @@ class API(base.Base): def get_network_topic(self, context, instance_id): try: -<<<<<<< TREE instance = self.get_instance(context, instance_id) + # TODO (dabo) Need to verify whether an internal_id or a db id + # id being passed; use get_instance or get, respectively. + #instance = self.get(context, instance_id) except exception.NotFound, e: -======= - instance = self.get(context, instance_id) - except exception.NotFound as e: ->>>>>>> MERGE-SOURCE logging.warning("Instance %d was not found in get_network_topic", instance_id) raise e -- cgit From 3d70b49a1c17bccfc6163198b2d99efb9a9829a7 Mon Sep 17 00:00:00 2001 From: Ed Leafe Date: Thu, 6 Jan 2011 13:02:32 -0600 Subject: commit before merging trunk --- nova/api/ec2/cloud.py | 2 +- nova/api/openstack/servers.py | 14 +++++++++++--- nova/compute/api.py | 18 ++++-------------- nova/compute/manager.py | 5 +++-- nova/db/api.py | 5 +++++ nova/db/sqlalchemy/api.py | 23 +++++++++++++++++++++++ nova/virt/xenapi/vmops.py | 7 +++++++ nova/virt/xenapi_conn.py | 3 +++ 8 files changed, 57 insertions(+), 20 deletions(-) (limited to 'nova') diff --git a/nova/api/ec2/cloud.py b/nova/api/ec2/cloud.py index 0c0027287..6619b5452 100644 --- a/nova/api/ec2/cloud.py +++ b/nova/api/ec2/cloud.py @@ -592,7 +592,7 @@ class CloudController(object): return {'reservationSet': self._format_instances(context)} def _format_run_instances(self, context, reservation_id): - i = self._format_instances(context, reservation_id) + i = self._format_instances(context, reservation_id=reservation_id) assert len(i) == 1 return i[0] diff --git a/nova/api/openstack/servers.py b/nova/api/openstack/servers.py index 05ba56c79..e0513e4c1 100644 --- a/nova/api/openstack/servers.py +++ b/nova/api/openstack/servers.py @@ -104,7 +104,7 @@ class Controller(wsgi.Controller): def show(self, req, id): """ Returns server details by server id """ try: - instance = self.compute_api.get(req.environ['nova.context'], id) + instance = self.compute_api.get_instance(req.environ['nova.context'], id) return _translate_detail_keys(instance) except exception.NotFound: return faults.Fault(exc.HTTPNotFound()) @@ -142,8 +142,10 @@ class Controller(wsgi.Controller): return faults.Fault(exc.HTTPUnprocessableEntity()) update_dict = {} + func = None if 'adminPass' in inst_dict['server']: update_dict['admin_pass'] = inst_dict['server']['adminPass'] + func = self.compute_api.set_admin_password if 'name' in inst_dict['server']: update_dict['display_name'] = inst_dict['server']['name'] @@ -152,9 +154,15 @@ class Controller(wsgi.Controller): # The ID passed in is actually the internal_id of the # instance, not the value of the id column in the DB. instance = self.compute_api.get_instance(ctxt, id) - self.compute_api.update_instance(ctxt, instance.id, **update_dict) + self.compute_api.update(ctxt, instance.id, **update_dict) except exception.NotFound: return faults.Fault(exc.HTTPNotFound()) + + logging.error("ZZZZ func=%s" % func) + logging.error("ZZZZ UPD=%s" % id) + + if func: + func(ctxt, id) return exc.HTTPNoContent() def action(self, req, id): @@ -225,4 +233,4 @@ class Controller(wsgi.Controller): def actions(self, req, id): """Permit Admins to retrieve server actions.""" ctxt = req.environ["nova.context"] - return self.compute_api.get_actions(ctxt, id) + return self.compute_api.get_actions(ctxt, id_val) diff --git a/nova/compute/api.py b/nova/compute/api.py index 19cdf2d0a..a4345f337 100644 --- a/nova/compute/api.py +++ b/nova/compute/api.py @@ -62,9 +62,6 @@ class API(base.Base): def get_network_topic(self, context, instance_id): try: instance = self.get_instance(context, instance_id) - # TODO (dabo) Need to verify whether an internal_id or a db id - # id being passed; use get_instance or get, respectively. - #instance = self.get(context, instance_id) except exception.NotFound, e: logging.warning("Instance %d was not found in get_network_topic", instance_id) @@ -223,10 +220,6 @@ class API(base.Base): logging.debug('Going to try and terminate %s' % instance_id) try: instance = self.get_instance(context, instance_id) - #TODO: (dabo) resolve that this is the correct call: - # get_instance vs. get. Depends on whether we get an internal_id - # or an actual db id. - #instance = self.get(context, instance_id) except exception.NotFound, e: logging.warning(_('Instance % was not found during terminate'), instance_id) @@ -252,7 +245,7 @@ class API(base.Base): else: self.db.instance_destroy(context, instance_id) - def get(self, context, instance_id): + def get_instance(self, context, instance_id): """Get a single instance with the given ID.""" return self.db.instance_get_by_id(context, instance_id) @@ -276,9 +269,6 @@ class API(base.Base): project_id) return self.db.instance_get_all(context) - def get_instance(self, context, instance_id): - return self.db.instance_get_by_internal_id(context, instance_id) - def _cast_compute_message(self, method, context, instance_id): """Generic handler for RPC calls to compute.""" instance = self.get_instance(context, instance_id) @@ -309,7 +299,7 @@ class API(base.Base): def get_actions(self, context, instance_id): """Retrieve actions for the given instance.""" - instance = self.db.instance_get_by_internal_id(context, instance_id) + instance = self.db.instance_get_by_id(context, instance_id) return self.db.instance_get_actions(context, instance['id']) def suspend(self, context, instance_id): @@ -337,7 +327,7 @@ class API(base.Base): raise exception.ApiError(_("Invalid device specified: %s. " "Example device: /dev/vdb") % device) self.volume_api.check_attach(context, volume_id) - instance = self.get(context, instance_id) + instance = self.get_instance(context, instance_id) host = instance['host'] rpc.cast(context, self.db.queue_get_for(context, FLAGS.compute_topic, host), @@ -360,6 +350,6 @@ class API(base.Base): return instance def associate_floating_ip(self, context, instance_id, address): - instance = self.get(context, instance_id) + instance = self.get_instance(context, instance_id) self.network_api.associate_floating_ip(context, address, instance['fixed_ip']) diff --git a/nova/compute/manager.py b/nova/compute/manager.py index 3e6d3eab0..201fffc68 100644 --- a/nova/compute/manager.py +++ b/nova/compute/manager.py @@ -83,7 +83,8 @@ class ComputeManager(manager.Manager): # FIXME(ja): include other fields from state? instance_ref = self.db.instance_get(context, instance_id) try: - info = self.driver.get_info(instance_ref['name']) + #info = self.driver.get_info(instance_ref['name']) + info = self.driver.get_info(instance_ref) state = info['state'] except exception.NotFound: state = power_state.NOSTATE @@ -259,7 +260,7 @@ class ComputeManager(manager.Manager): if instance_ref['state'] != power_state.RUNNING: logging.warn('trying to reset the password on a non-running ' 'instance: %s (state: %s expected: %s)', - instance_ref['internal_id'], + instance_ref['id'], instance_ref['state'], power_state.RUNNING) diff --git a/nova/db/api.py b/nova/db/api.py index 0fa5eb1e8..f5c7eab91 100644 --- a/nova/db/api.py +++ b/nova/db/api.py @@ -358,6 +358,11 @@ def instance_get_by_id(context, instance_id): return IMPL.instance_get_by_id(context, instance_id) +def instance_get_by_internal_id(context, internal_id): + """Get an instance by internal id.""" + return IMPL.instance_get_by_internal_id(context, internal_id) + + def instance_is_vpn(context, instance_id): """True if instance is a vpn.""" return IMPL.instance_is_vpn(context, instance_id) diff --git a/nova/db/sqlalchemy/api.py b/nova/db/sqlalchemy/api.py index aaa07e3c9..34c73490e 100644 --- a/nova/db/sqlalchemy/api.py +++ b/nova/db/sqlalchemy/api.py @@ -670,6 +670,29 @@ def instance_get(context, instance_id, session=None): return result +@require_context +def instance_get_by_internal_id(context, internal_id): + session = get_session() + + if is_admin_context(context): + result = session.query(models.Instance).\ + options(joinedload('security_groups')).\ + filter_by(internal_id=internal_id).\ + filter_by(deleted=can_read_deleted(context)).\ + first() + elif is_user_context(context): + result = session.query(models.Instance).\ + options(joinedload('security_groups')).\ + filter_by(project_id=context.project_id).\ + filter_by(internal_id=internal_id).\ + filter_by(deleted=False).\ + first() + if not result: + raise exception.NotFound(_('Instance %s not found') % (internal_id)) + + return result + + @require_admin_context def instance_get_all(context): session = get_session() diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py index 64855001b..e092601b9 100644 --- a/nova/virt/xenapi/vmops.py +++ b/nova/virt/xenapi/vmops.py @@ -130,17 +130,21 @@ class VMOps(object): """Refactored out the common code of many methods that receive either a vm name or a vm instance, and want a vm instance in return. """ + logging.error("ZZZZ opaq instance_or_vm=%s" % instance_or_vm) vm = None try: if instance_or_vm.startswith("OpaqueRef:"): + logging.error("ZZZZ opaq startswith") # Got passed an opaque ref; return it return instance_or_vm else: # Must be the instance name + logging.error("ZZZZ opaq inst name") instance_name = instance_or_vm except AttributeError: # Not a string; must be a vm instance instance_name = instance_or_vm.name + logging.error("ZZZZ opaq instance, name=%s" % instance_name) vm = VMHelper.lookup(self._session, instance_name) if vm is None: raise Exception(_('Instance not present %s') % instance_name) @@ -302,6 +306,9 @@ class VMOps(object): def get_info(self, instance): """Return data about VM instance""" + + logging.error("ZZZZ get_info instance=%s" % instance) + vm = self._get_vm_opaque_ref(instance) rec = self._session.get_xenapi().VM.get_record(vm) return VMHelper.compile_info(rec) diff --git a/nova/virt/xenapi_conn.py b/nova/virt/xenapi_conn.py index 0cba2812d..0ceac8c97 100644 --- a/nova/virt/xenapi_conn.py +++ b/nova/virt/xenapi_conn.py @@ -170,6 +170,9 @@ class XenAPIConnection(object): def get_info(self, instance_id): """Return data about VM instance""" + + logging.error("ZZZZ conn get_info id=%s" % instance_id) + return self._vmops.get_info(instance_id) def get_diagnostics(self, instance): -- cgit From e66f3017373dcf9135c53ae4d510b0b2a5dcecf0 Mon Sep 17 00:00:00 2001 From: Ed Leafe Date: Thu, 6 Jan 2011 15:53:11 -0600 Subject: Got the basic 'set admin password' stuff working --- nova/api/openstack/servers.py | 17 ++++++++--------- nova/compute/manager.py | 3 --- nova/exception.py | 4 ++++ nova/virt/xenapi/vmops.py | 22 +++++++++------------- nova/virt/xenapi_conn.py | 16 ++-------------- 5 files changed, 23 insertions(+), 39 deletions(-) (limited to 'nova') diff --git a/nova/api/openstack/servers.py b/nova/api/openstack/servers.py index e0513e4c1..bc89f696c 100644 --- a/nova/api/openstack/servers.py +++ b/nova/api/openstack/servers.py @@ -104,7 +104,8 @@ class Controller(wsgi.Controller): def show(self, req, id): """ Returns server details by server id """ try: - instance = self.compute_api.get_instance(req.environ['nova.context'], id) + instance = self.compute_api.get_instance( + req.environ['nova.context'], id) return _translate_detail_keys(instance) except exception.NotFound: return faults.Fault(exc.HTTPNotFound()) @@ -141,6 +142,7 @@ class Controller(wsgi.Controller): if not inst_dict: return faults.Fault(exc.HTTPUnprocessableEntity()) + ctxt = req.environ['nova.context'] update_dict = {} func = None if 'adminPass' in inst_dict['server']: @@ -148,21 +150,18 @@ class Controller(wsgi.Controller): func = self.compute_api.set_admin_password if 'name' in inst_dict['server']: update_dict['display_name'] = inst_dict['server']['name'] - + if func: + try: + func(ctxt, id) + except exception.TimeoutException, e: + return exc.HTTPRequestTimeout() try: - ctxt = req.environ['nova.context'] # The ID passed in is actually the internal_id of the # instance, not the value of the id column in the DB. instance = self.compute_api.get_instance(ctxt, id) self.compute_api.update(ctxt, instance.id, **update_dict) except exception.NotFound: return faults.Fault(exc.HTTPNotFound()) - - logging.error("ZZZZ func=%s" % func) - logging.error("ZZZZ UPD=%s" % id) - - if func: - func(ctxt, id) return exc.HTTPNoContent() def action(self, req, id): diff --git a/nova/compute/manager.py b/nova/compute/manager.py index 201fffc68..52acfebea 100644 --- a/nova/compute/manager.py +++ b/nova/compute/manager.py @@ -83,7 +83,6 @@ class ComputeManager(manager.Manager): # FIXME(ja): include other fields from state? instance_ref = self.db.instance_get(context, instance_id) try: - #info = self.driver.get_info(instance_ref['name']) info = self.driver.get_info(instance_ref) state = info['state'] except exception.NotFound: @@ -266,8 +265,6 @@ class ComputeManager(manager.Manager): logging.debug('instance %s: setting admin password', instance_ref['name']) - self.db.instance_set_state(context, instance_id, - power_state.NOSTATE, 'setting_password') if new_pass is None: # Generate a random password new_pass = self._generate_password(FLAGS.password_length) diff --git a/nova/exception.py b/nova/exception.py index 277033e0f..52bf2a2a7 100644 --- a/nova/exception.py +++ b/nova/exception.py @@ -77,6 +77,10 @@ class InvalidInputException(Error): pass +class TimeoutException(Error): + pass + + def wrap_exception(f): def _wrap(*args, **kw): try: diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py index e092601b9..5f1654a49 100644 --- a/nova/virt/xenapi/vmops.py +++ b/nova/virt/xenapi/vmops.py @@ -130,21 +130,17 @@ class VMOps(object): """Refactored out the common code of many methods that receive either a vm name or a vm instance, and want a vm instance in return. """ - logging.error("ZZZZ opaq instance_or_vm=%s" % instance_or_vm) vm = None try: if instance_or_vm.startswith("OpaqueRef:"): - logging.error("ZZZZ opaq startswith") # Got passed an opaque ref; return it return instance_or_vm else: # Must be the instance name - logging.error("ZZZZ opaq inst name") instance_name = instance_or_vm except AttributeError: # Not a string; must be a vm instance instance_name = instance_or_vm.name - logging.error("ZZZZ opaq instance, name=%s" % instance_name) vm = VMHelper.lookup(self._session, instance_name) if vm is None: raise Exception(_('Instance not present %s') % instance_name) @@ -210,9 +206,6 @@ class VMOps(object): simple Diffie-Hellman class instead of the more advanced one in M2Crypto for compatibility with the agent code. """ - - logging.error("ZZZZ SET PASS CALLED") - # Need to uniquely identify this request. transaction_id = str(uuid.uuid4()) # The simple Diffie-Hellman class is used to manage key exchange. @@ -306,9 +299,6 @@ class VMOps(object): def get_info(self, instance): """Return data about VM instance""" - - logging.error("ZZZZ get_info instance=%s" % instance) - vm = self._get_vm_opaque_ref(instance) rec = self._session.get_xenapi().VM.get_record(vm) return VMHelper.compile_info(rec) @@ -370,13 +360,14 @@ class VMOps(object): def _make_agent_call(self, method, vm, path, addl_args={}): """Abstracts out the interaction with the agent xenapi plugin.""" - return self._make_plugin_call('agent.py', method=method, vm=vm, + return self._make_plugin_call('agent', method=method, vm=vm, path=path, addl_args=addl_args) def _make_plugin_call(self, plugin, method, vm, path, addl_args={}): """Abstracts out the process of calling a method of a xenapi plugin. Any errors raised by the plugin will in turn raise a RuntimeError here. """ + instance_id = vm.id vm = self._get_vm_opaque_ref(vm) rec = self._session.get_xenapi().VM.get_record(vm) args = {'dom_id': rec['domid'], 'path': path} @@ -386,9 +377,14 @@ class VMOps(object): args['testing_mode'] = 'true' try: task = self._session.async_call_plugin(plugin, method, args) - ret = self._session.wait_for_task(0, task) + ret = self._session.wait_for_task(instance_id, task) except self.XenAPI.Failure, e: - raise RuntimeError("%s" % e.details[-1]) + err_trace = e.details[-1] + err_msg = err_trace.splitlines()[-1] + if 'TIMEOUT:' in err_msg: + raise exception.TimeoutException(err_msg) + else: + raise RuntimeError("%s" % e.details[-1]) return ret def add_to_xenstore(self, vm, path, key, value): diff --git a/nova/virt/xenapi_conn.py b/nova/virt/xenapi_conn.py index 0ceac8c97..c9428e3a6 100644 --- a/nova/virt/xenapi_conn.py +++ b/nova/virt/xenapi_conn.py @@ -170,9 +170,6 @@ class XenAPIConnection(object): def get_info(self, instance_id): """Return data about VM instance""" - - logging.error("ZZZZ conn get_info id=%s" % instance_id) - return self._vmops.get_info(instance_id) def get_diagnostics(self, instance): @@ -251,7 +248,8 @@ class XenAPISession(object): def _poll_task(self, id, task, done): """Poll the given XenAPI task, and fire the given action if we - get a result.""" + get a result. + """ try: name = self._session.xenapi.task.get_name_label(task) status = self._session.xenapi.task.get_status(task) @@ -278,16 +276,6 @@ class XenAPISession(object): error_info)) done.send_exception(self.XenAPI.Failure(error_info)) db.instance_action_create(context.get_admin_context(), action) -# import sqlalchemy -# from sqlalchemy.exc import IntegrityError as IntegrityError -# try: -# db.instance_action_create(context.get_admin_context(), action) -# except IntegrityError: -# # Some methods don't pass unique IDs, so the call to -# # instance_action_create() will raise IntegrityErrors. Rather -# # than bomb out, I'm explicitly silencing them so that the -# # code can continue to work until they fix that method. -# pass except self.XenAPI.Failure, exc: logging.warn(exc) -- cgit From dea9f51d65ce0d5c3c4ea328a2231499c71719d6 Mon Sep 17 00:00:00 2001 From: Ed Leafe Date: Thu, 6 Jan 2011 17:37:02 -0500 Subject: various cleanup and fixes --- nova/api/openstack/servers.py | 7 ++----- nova/compute/api.py | 2 +- nova/db/sqlalchemy/api.py | 23 ----------------------- 3 files changed, 3 insertions(+), 29 deletions(-) (limited to 'nova') diff --git a/nova/api/openstack/servers.py b/nova/api/openstack/servers.py index bc89f696c..9eaef2b40 100644 --- a/nova/api/openstack/servers.py +++ b/nova/api/openstack/servers.py @@ -156,10 +156,7 @@ class Controller(wsgi.Controller): except exception.TimeoutException, e: return exc.HTTPRequestTimeout() try: - # The ID passed in is actually the internal_id of the - # instance, not the value of the id column in the DB. - instance = self.compute_api.get_instance(ctxt, id) - self.compute_api.update(ctxt, instance.id, **update_dict) + self.compute_api.update(ctxt, id, **update_dict) except exception.NotFound: return faults.Fault(exc.HTTPNotFound()) return exc.HTTPNoContent() @@ -232,4 +229,4 @@ class Controller(wsgi.Controller): def actions(self, req, id): """Permit Admins to retrieve server actions.""" ctxt = req.environ["nova.context"] - return self.compute_api.get_actions(ctxt, id_val) + return self.compute_api.get_actions(ctxt, id) diff --git a/nova/compute/api.py b/nova/compute/api.py index a4345f337..232eddb64 100644 --- a/nova/compute/api.py +++ b/nova/compute/api.py @@ -217,7 +217,7 @@ class API(base.Base): return self.db.instance_update(context, instance_id, kwargs) def delete(self, context, instance_id): - logging.debug('Going to try and terminate %s' % instance_id) + logging.debug('Going to try to terminate %s' % instance_id) try: instance = self.get_instance(context, instance_id) except exception.NotFound, e: diff --git a/nova/db/sqlalchemy/api.py b/nova/db/sqlalchemy/api.py index 34c73490e..aaa07e3c9 100644 --- a/nova/db/sqlalchemy/api.py +++ b/nova/db/sqlalchemy/api.py @@ -670,29 +670,6 @@ def instance_get(context, instance_id, session=None): return result -@require_context -def instance_get_by_internal_id(context, internal_id): - session = get_session() - - if is_admin_context(context): - result = session.query(models.Instance).\ - options(joinedload('security_groups')).\ - filter_by(internal_id=internal_id).\ - filter_by(deleted=can_read_deleted(context)).\ - first() - elif is_user_context(context): - result = session.query(models.Instance).\ - options(joinedload('security_groups')).\ - filter_by(project_id=context.project_id).\ - filter_by(internal_id=internal_id).\ - filter_by(deleted=False).\ - first() - if not result: - raise exception.NotFound(_('Instance %s not found') % (internal_id)) - - return result - - @require_admin_context def instance_get_all(context): session = get_session() -- cgit From e01123943e7fbe81d7cb40325cde6c517bb2ffd9 Mon Sep 17 00:00:00 2001 From: Ed Leafe Date: Fri, 7 Jan 2011 08:36:11 -0500 Subject: grabbed the get_info fix from my other branch --- nova/virt/xenapi/vmops.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) (limited to 'nova') diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py index 5f1654a49..65d0360c8 100644 --- a/nova/virt/xenapi/vmops.py +++ b/nova/virt/xenapi/vmops.py @@ -138,8 +138,9 @@ class VMOps(object): else: # Must be the instance name instance_name = instance_or_vm - except AttributeError: + except (AttributeError, KeyError): # Not a string; must be a vm instance + # Note the the KeyError will only happen with fakes.py instance_name = instance_or_vm.name vm = VMHelper.lookup(self._session, instance_name) if vm is None: @@ -297,9 +298,12 @@ class VMOps(object): task = self._session.call_xenapi('Async.VM.resume', vm, False, True) self._wait_with_callback(task, callback) - def get_info(self, instance): + def get_info(self, instance_id): """Return data about VM instance""" - vm = self._get_vm_opaque_ref(instance) + vm = VMHelper.lookup(self._session, instance_id) + if vm is None: + raise exception.NotFound(_('Instance not' + ' found %s') % instance_id) rec = self._session.get_xenapi().VM.get_record(vm) return VMHelper.compile_info(rec) -- cgit From eaa5b5994891eee0280b750dff221a4b54932eb9 Mon Sep 17 00:00:00 2001 From: Ed Leafe Date: Fri, 7 Jan 2011 10:23:48 -0600 Subject: getting ready to push for merge prop --- nova/api/openstack/servers.py | 4 ++-- nova/compute/api.py | 18 +++++++++--------- nova/compute/manager.py | 3 +-- nova/virt/xenapi/vmops.py | 16 ++++++++++------ 4 files changed, 22 insertions(+), 19 deletions(-) (limited to 'nova') diff --git a/nova/api/openstack/servers.py b/nova/api/openstack/servers.py index a426a721d..280e2349e 100644 --- a/nova/api/openstack/servers.py +++ b/nova/api/openstack/servers.py @@ -104,7 +104,7 @@ class Controller(wsgi.Controller): def show(self, req, id): """ Returns server details by server id """ try: - instance = self.compute_api.get_instance( + instance = self.compute_api.get( req.environ['nova.context'], id) return _translate_detail_keys(instance) except exception.NotFound: @@ -158,7 +158,7 @@ class Controller(wsgi.Controller): try: # The ID passed in is actually the internal_id of the # instance, not the value of the id column in the DB. - instance = self.compute_api.get_instance(ctxt, id) + instance = self.compute_api.get(ctxt, id) self.compute_api.update(ctxt, instance.id, **update_dict) except exception.NotFound: return faults.Fault(exc.HTTPNotFound()) diff --git a/nova/compute/api.py b/nova/compute/api.py index 78ffcca7a..106c3f7f0 100644 --- a/nova/compute/api.py +++ b/nova/compute/api.py @@ -61,7 +61,7 @@ class API(base.Base): def get_network_topic(self, context, instance_id): try: - instance = self.get_instance(context, instance_id) + instance = self.get(context, instance_id) except exception.NotFound, e: logging.warning("Instance %d was not found in get_network_topic", instance_id) @@ -220,7 +220,7 @@ class API(base.Base): def delete(self, context, instance_id): logging.debug('Going to try and terminate %s' % instance_id) try: - instance = self.get_instance(context, instance_id) + instance = self.get(context, instance_id) except exception.NotFound, e: logging.warning(_('Instance % was not found during terminate'), instance_id) @@ -246,7 +246,7 @@ class API(base.Base): else: self.db.instance_destroy(context, instance_id) - def get_instance(self, context, instance_id): + def get(self, context, instance_id): """Get a single instance with the given ID.""" return self.db.instance_get_by_id(context, instance_id) @@ -272,7 +272,7 @@ class API(base.Base): def _cast_compute_message(self, method, context, instance_id): """Generic handler for RPC calls to compute.""" - instance = self.get_instance(context, instance_id) + instance = self.get(context, instance_id) host = instance['host'] rpc.cast(context, self.db.queue_get_for(context, FLAGS.compute_topic, host), @@ -328,7 +328,7 @@ class API(base.Base): lock the instance with instance_id """ - instance = self.get_instance(context, instance_id) + instance = self.get(context, instance_id) host = instance['host'] rpc.cast(context, self.db.queue_get_for(context, FLAGS.compute_topic, host), @@ -340,7 +340,7 @@ class API(base.Base): unlock the instance with instance_id """ - instance = self.get_instance(context, instance_id) + instance = self.get(context, instance_id) host = instance['host'] rpc.cast(context, self.db.queue_get_for(context, FLAGS.compute_topic, host), @@ -352,7 +352,7 @@ class API(base.Base): return the boolean state of (instance with instance_id)'s lock """ - instance = self.get_instance(context, instance_id) + instance = self.get(context, instance_id) return instance['locked'] def attach_volume(self, context, instance_id, volume_id, device): @@ -360,7 +360,7 @@ class API(base.Base): raise exception.ApiError(_("Invalid device specified: %s. " "Example device: /dev/vdb") % device) self.volume_api.check_attach(context, volume_id) - instance = self.get_instance(context, instance_id) + instance = self.get(context, instance_id) host = instance['host'] rpc.cast(context, self.db.queue_get_for(context, FLAGS.compute_topic, host), @@ -383,6 +383,6 @@ class API(base.Base): return instance def associate_floating_ip(self, context, instance_id, address): - instance = self.get_instance(context, instance_id) + instance = self.get(context, instance_id) self.network_api.associate_floating_ip(context, address, instance['fixed_ip']) diff --git a/nova/compute/manager.py b/nova/compute/manager.py index 10219833b..5d677b023 100644 --- a/nova/compute/manager.py +++ b/nova/compute/manager.py @@ -289,8 +289,6 @@ class ComputeManager(manager.Manager): """Set the root/admin password for an instance on this server.""" context = context.elevated() instance_ref = self.db.instance_get(context, instance_id) - self._update_state(context, instance_id) - if instance_ref['state'] != power_state.RUNNING: logging.warn('trying to reset the password on a non-running ' 'instance: %s (state: %s expected: %s)', @@ -303,6 +301,7 @@ class ComputeManager(manager.Manager): if new_pass is None: # Generate a random password new_pass = self._generate_password(FLAGS.password_length) + self.driver.set_admin_password(instance_ref, new_pass) self._update_state(context, instance_id) diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py index 5f1654a49..3df561e7c 100644 --- a/nova/virt/xenapi/vmops.py +++ b/nova/virt/xenapi/vmops.py @@ -137,13 +137,17 @@ class VMOps(object): return instance_or_vm else: # Must be the instance name - instance_name = instance_or_vm - except AttributeError: - # Not a string; must be a vm instance - instance_name = instance_or_vm.name - vm = VMHelper.lookup(self._session, instance_name) + instance = instance_or_vm + except (AttributeError, KeyError): + # Note the the KeyError will only happen with fakes.py + # Not a string; must be an ID or a vm instance + if isinstance(instance_or_vm, (int, long)): + instance = instance_or_vm + else: + instance = instance_or_vm.name + vm = VMHelper.lookup(self._session, instance) if vm is None: - raise Exception(_('Instance not present %s') % instance_name) + raise Exception(_('Instance not present %s') % instance) return vm def snapshot(self, instance, name): -- cgit From 147693e45c7be174c54e39160869ca9a83bb4fff Mon Sep 17 00:00:00 2001 From: Ed Leafe Date: Fri, 7 Jan 2011 11:04:53 -0600 Subject: Additional cleanup prior to pushing --- nova/api/__init__.py | 14 ++--------- nova/api/openstack/servers.py | 2 -- nova/compute/api.py | 54 +++++++++++++------------------------------ nova/db/api.py | 5 ---- nova/db/sqlalchemy/api.py | 23 ------------------ nova/virt/xenapi/vmops.py | 6 +++++ nova/virt/xenapi_conn.py | 1 - 7 files changed, 24 insertions(+), 81 deletions(-) (limited to 'nova') diff --git a/nova/api/__init__.py b/nova/api/__init__.py index ff936bed2..26fed847b 100644 --- a/nova/api/__init__.py +++ b/nova/api/__init__.py @@ -59,23 +59,13 @@ class API(wsgi.Router): mapper.connect("/", controller=self.osapi_versions, conditions=osapi_subdomain) - mapper.connect("/v1.0/{path_info:.*}", controller=openstack.API()) + mapper.connect("/v1.0/{path_info:.*}", controller=openstack.API(), + conditions=osapi_subdomain) mapper.connect("/", controller=self.ec2api_versions, conditions=ec2api_subdomain) mapper.connect("/services/{path_info:.*}", controller=ec2.API(), conditions=ec2api_subdomain) - -# mapper.connect("/", controller=self.osapi_versions, -# conditions=osapi_subdomain) -# mapper.connect("/v1.0/{path_info:.*}", controller=openstack.API(), -# conditions=osapi_subdomain) -# -# mapper.connect("/", controller=self.ec2api_versions, -# conditions=ec2api_subdomain) -# mapper.connect("/services/{path_info:.*}", controller=ec2.API(), -# conditions=ec2api_subdomain) - mrh = metadatarequesthandler.MetadataRequestHandler() for s in ['/latest', '/2009-04-04', diff --git a/nova/api/openstack/servers.py b/nova/api/openstack/servers.py index 280e2349e..6a35567ff 100644 --- a/nova/api/openstack/servers.py +++ b/nova/api/openstack/servers.py @@ -156,8 +156,6 @@ class Controller(wsgi.Controller): except exception.TimeoutException, e: return exc.HTTPRequestTimeout() try: - # The ID passed in is actually the internal_id of the - # instance, not the value of the id column in the DB. instance = self.compute_api.get(ctxt, id) self.compute_api.update(ctxt, instance.id, **update_dict) except exception.NotFound: diff --git a/nova/compute/api.py b/nova/compute/api.py index 106c3f7f0..a894a0ce3 100644 --- a/nova/compute/api.py +++ b/nova/compute/api.py @@ -62,7 +62,7 @@ class API(base.Base): def get_network_topic(self, context, instance_id): try: instance = self.get(context, instance_id) - except exception.NotFound, e: + except exception.NotFound as e: logging.warning("Instance %d was not found in get_network_topic", instance_id) raise e @@ -195,7 +195,7 @@ class API(base.Base): """ try: db.security_group_get_by_name(context, context.project_id, - 'default') + 'default') except exception.NotFound: values = {'name': 'default', 'description': 'default', @@ -218,12 +218,12 @@ class API(base.Base): return self.db.instance_update(context, instance_id, kwargs) def delete(self, context, instance_id): - logging.debug('Going to try and terminate %s' % instance_id) + logging.debug('Going to try to terminate %s' % instance_id) try: instance = self.get(context, instance_id) - except exception.NotFound, e: + except exception.NotFound as e: logging.warning(_('Instance % was not found during terminate'), - instance_id) + instance_id) raise e if (instance['state_description'] == 'terminating'): @@ -239,10 +239,8 @@ class API(base.Base): host = instance['host'] if host: - rpc.cast(context, - self.db.queue_get_for(context, FLAGS.compute_topic, host), - {"method": "terminate_instance", - "args": {"instance_id": instance_id}}) + self._cast_compute_message('terminate_instance', context, + instance_id) else: self.db.instance_destroy(context, instance_id) @@ -274,9 +272,9 @@ class API(base.Base): """Generic handler for RPC calls to compute.""" instance = self.get(context, instance_id) host = instance['host'] - rpc.cast(context, - self.db.queue_get_for(context, FLAGS.compute_topic, host), - {'method': method, 'args': {'instance_id': instance['id']}}) + queue = self.db.queue_get_for(context, FLAGS.compute_topic, host) + kwargs = {'method': method, 'args': {'instance_id': instance['id']}} + rpc.cast(context, queue, kwargs) def snapshot(self, context, instance_id, name): """Snapshot the given instance.""" @@ -300,8 +298,7 @@ class API(base.Base): def get_actions(self, context, instance_id): """Retrieve actions for the given instance.""" - instance = self.db.instance_get_by_id(context, instance_id) - return self.db.instance_get_actions(context, instance['id']) + return self.db.instance_get_actions(context, instance_id) def suspend(self, context, instance_id): """suspend the instance with instance_id""" @@ -324,34 +321,15 @@ class API(base.Base): self._cast_compute_message('set_admin_password', context, instance_id) def lock(self, context, instance_id): - """ - lock the instance with instance_id - - """ - instance = self.get(context, instance_id) - host = instance['host'] - rpc.cast(context, - self.db.queue_get_for(context, FLAGS.compute_topic, host), - {"method": "lock_instance", - "args": {"instance_id": instance['id']}}) + """lock the instance with instance_id""" + self._cast_compute_message('lock_instance', context, instance_id) def unlock(self, context, instance_id): - """ - unlock the instance with instance_id - - """ - instance = self.get(context, instance_id) - host = instance['host'] - rpc.cast(context, - self.db.queue_get_for(context, FLAGS.compute_topic, host), - {"method": "unlock_instance", - "args": {"instance_id": instance['id']}}) + """unlock the instance with instance_id""" + self._cast_compute_message('unlock_instance', context, instance_id) def get_lock(self, context, instance_id): - """ - return the boolean state of (instance with instance_id)'s lock - - """ + """return the boolean state of (instance with instance_id)'s lock""" instance = self.get(context, instance_id) return instance['locked'] diff --git a/nova/db/api.py b/nova/db/api.py index f5c7eab91..0fa5eb1e8 100644 --- a/nova/db/api.py +++ b/nova/db/api.py @@ -358,11 +358,6 @@ def instance_get_by_id(context, instance_id): return IMPL.instance_get_by_id(context, instance_id) -def instance_get_by_internal_id(context, internal_id): - """Get an instance by internal id.""" - return IMPL.instance_get_by_internal_id(context, internal_id) - - def instance_is_vpn(context, instance_id): """True if instance is a vpn.""" return IMPL.instance_is_vpn(context, instance_id) diff --git a/nova/db/sqlalchemy/api.py b/nova/db/sqlalchemy/api.py index 0e5c14275..45427597a 100644 --- a/nova/db/sqlalchemy/api.py +++ b/nova/db/sqlalchemy/api.py @@ -670,29 +670,6 @@ def instance_get(context, instance_id, session=None): return result -@require_context -def instance_get_by_internal_id(context, internal_id): - session = get_session() - - if is_admin_context(context): - result = session.query(models.Instance).\ - options(joinedload('security_groups')).\ - filter_by(internal_id=internal_id).\ - filter_by(deleted=can_read_deleted(context)).\ - first() - elif is_user_context(context): - result = session.query(models.Instance).\ - options(joinedload('security_groups')).\ - filter_by(project_id=context.project_id).\ - filter_by(internal_id=internal_id).\ - filter_by(deleted=False).\ - first() - if not result: - raise exception.NotFound(_('Instance %s not found') % (internal_id)) - - return result - - @require_admin_context def instance_get_all(context): session = get_session() diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py index 3df561e7c..210129771 100644 --- a/nova/virt/xenapi/vmops.py +++ b/nova/virt/xenapi/vmops.py @@ -516,6 +516,12 @@ class SimpleDH(object): the openssl binary be installed on the system on which this is run, as it uses that to handle the encryption and decryption. If openssl is not available, a RuntimeError will be raised. + + Please note that nova already uses the M2Crypto library for most + cryptographic functions, and that it includes a Diffie-Hellman + implementation. However, that is a much more complex implementation, + and is not compatible with the DH algorithm that the agent uses. Hence + the need for this 'simple' version. """ def __init__(self, prime=None, base=None, secret=None): """You can specify the values for prime and base if you wish; diff --git a/nova/virt/xenapi_conn.py b/nova/virt/xenapi_conn.py index c9428e3a6..3ea81474b 100644 --- a/nova/virt/xenapi_conn.py +++ b/nova/virt/xenapi_conn.py @@ -276,7 +276,6 @@ class XenAPISession(object): error_info)) done.send_exception(self.XenAPI.Failure(error_info)) db.instance_action_create(context.get_admin_context(), action) - except self.XenAPI.Failure, exc: logging.warn(exc) done.send_exception(*sys.exc_info()) -- cgit From 5d9ad54cc38283d0b946779f4235f54370b12489 Mon Sep 17 00:00:00 2001 From: Ed Leafe Date: Fri, 7 Jan 2011 15:50:43 -0500 Subject: incorporated changes suggested by eday --- nova/api/openstack/servers.py | 9 +++------ nova/compute/api.py | 13 +++++++------ nova/compute/manager.py | 1 + 3 files changed, 11 insertions(+), 12 deletions(-) (limited to 'nova') diff --git a/nova/api/openstack/servers.py b/nova/api/openstack/servers.py index c71cb8562..c97be86a2 100644 --- a/nova/api/openstack/servers.py +++ b/nova/api/openstack/servers.py @@ -144,17 +144,14 @@ class Controller(wsgi.Controller): ctxt = req.environ['nova.context'] update_dict = {} - func = None if 'adminPass' in inst_dict['server']: update_dict['admin_pass'] = inst_dict['server']['adminPass'] - func = self.compute_api.set_admin_password - if 'name' in inst_dict['server']: - update_dict['display_name'] = inst_dict['server']['name'] - if func: try: - func(ctxt, id) + self.compute_api.set_admin_password(ctxt, id) except exception.TimeoutException, e: return exc.HTTPRequestTimeout() + if 'name' in inst_dict['server']: + update_dict['display_name'] = inst_dict['server']['name'] try: self.compute_api.update(ctxt, id, **update_dict) except exception.NotFound: diff --git a/nova/compute/api.py b/nova/compute/api.py index a894a0ce3..d90b59de6 100644 --- a/nova/compute/api.py +++ b/nova/compute/api.py @@ -239,8 +239,8 @@ class API(base.Base): host = instance['host'] if host: - self._cast_compute_message('terminate_instance', context, - instance_id) + self._cast_compute_message('snapshot_instance', context, + instance_id, host) else: self.db.instance_destroy(context, instance_id) @@ -268,12 +268,13 @@ class API(base.Base): project_id) return self.db.instance_get_all(context) - def _cast_compute_message(self, method, context, instance_id): + def _cast_compute_message(self, method, context, instance_id, host=None): """Generic handler for RPC calls to compute.""" - instance = self.get(context, instance_id) - host = instance['host'] + if not host: + instance = self.get(context, instance_id) + host = instance['host'] queue = self.db.queue_get_for(context, FLAGS.compute_topic, host) - kwargs = {'method': method, 'args': {'instance_id': instance['id']}} + kwargs = {'method': method, 'args': {'instance_id': instance_id}} rpc.cast(context, queue, kwargs) def snapshot(self, context, instance_id, name): diff --git a/nova/compute/manager.py b/nova/compute/manager.py index 5d677b023..7d4a097bd 100644 --- a/nova/compute/manager.py +++ b/nova/compute/manager.py @@ -285,6 +285,7 @@ class ComputeManager(manager.Manager): self.driver.snapshot(instance_ref, name) @exception.wrap_exception + @checks_instance_lock def set_admin_password(self, context, instance_id, new_pass=None): """Set the root/admin password for an instance on this server.""" context = context.elevated() -- cgit From 18b8d8307d0fc008f62dd8eeeedb351a954a3471 Mon Sep 17 00:00:00 2001 From: Ed Leafe Date: Fri, 7 Jan 2011 15:51:28 -0600 Subject: removed a merge conflict line I missed before --- nova/compute/manager.py | 1 - 1 file changed, 1 deletion(-) (limited to 'nova') diff --git a/nova/compute/manager.py b/nova/compute/manager.py index 8b5646a54..21b09e443 100644 --- a/nova/compute/manager.py +++ b/nova/compute/manager.py @@ -37,7 +37,6 @@ terminating it. import datetime import random import string ->>>>>>> MERGE-SOURCE import functools from nova import exception -- cgit From a0ec77b597713fd9a4be5bb7b892eba4ac53e625 Mon Sep 17 00:00:00 2001 From: Ed Leafe Date: Fri, 7 Jan 2011 15:56:32 -0600 Subject: Reverted formatting change no longer necessary --- nova/api/openstack/servers.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'nova') diff --git a/nova/api/openstack/servers.py b/nova/api/openstack/servers.py index d8a7ca161..764c843ac 100644 --- a/nova/api/openstack/servers.py +++ b/nova/api/openstack/servers.py @@ -104,8 +104,7 @@ class Controller(wsgi.Controller): def show(self, req, id): """ Returns server details by server id """ try: - instance = self.compute_api.get( - req.environ['nova.context'], id) + instance = self.compute_api.get(req.environ['nova.context'], id) return _translate_detail_keys(instance) except exception.NotFound: return faults.Fault(exc.HTTPNotFound()) -- cgit From bae57e82767b4877bae5c2dcb6fe052291d16b32 Mon Sep 17 00:00:00 2001 From: Ed Leafe Date: Mon, 10 Jan 2011 15:33:10 -0600 Subject: Fixed issues raised by reviews --- nova/compute/api.py | 2 +- nova/virt/xenapi/vmops.py | 15 +++++++-------- 2 files changed, 8 insertions(+), 9 deletions(-) (limited to 'nova') diff --git a/nova/compute/api.py b/nova/compute/api.py index a20dc59cb..10d7b67cf 100644 --- a/nova/compute/api.py +++ b/nova/compute/api.py @@ -297,7 +297,7 @@ class API(base.Base): host = instance['host'] if host: - self._cast_compute_message('snapshot_instance', context, + self._cast_compute_message('terminate_instance', context, instance_id, host) else: self.db.instance_destroy(context, instance_id) diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py index 78f35e9a7..206970c35 100644 --- a/nova/virt/xenapi/vmops.py +++ b/nova/virt/xenapi/vmops.py @@ -139,17 +139,19 @@ class VMOps(object): return instance_or_vm else: # Must be the instance name - instance = instance_or_vm + instance_name = instance_or_vm except (AttributeError, KeyError): # Note the the KeyError will only happen with fakes.py # Not a string; must be an ID or a vm instance if isinstance(instance_or_vm, (int, long)): - instance = instance_or_vm + ctx = context.get_admin_context() + instance_obj = db.instance_get_by_id(ctx, instance_or_vm) + instance_name = instance_obj.name else: - instance = instance_or_vm.name - vm = VMHelper.lookup(self._session, instance) + instance_name = instance_or_vm.name + vm = VMHelper.lookup(self._session, instance_name) if vm is None: - raise Exception(_('Instance not present %s') % instance) + raise Exception(_('Instance not present %s') % instance_name) return vm def snapshot(self, instance, name): @@ -378,9 +380,6 @@ class VMOps(object): rec = self._session.get_xenapi().VM.get_record(vm) args = {'dom_id': rec['domid'], 'path': path} args.update(addl_args) - # If the 'testing_mode' attribute is set, add that to the args. - if getattr(self, 'testing_mode', False): - args['testing_mode'] = 'true' try: task = self._session.async_call_plugin(plugin, method, args) ret = self._session.wait_for_task(instance_id, task) -- cgit From d91a06b4fea7e45fd2e9abe35803cd9deb5d8e92 Mon Sep 17 00:00:00 2001 From: Ed Leafe Date: Tue, 11 Jan 2011 12:17:39 -0600 Subject: Removed unneeded SimpleDH code from agent plugin. Improved handling of plugin call failures. --- nova/virt/xenapi/vmops.py | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) (limited to 'nova') diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py index 206970c35..c10943aa1 100644 --- a/nova/virt/xenapi/vmops.py +++ b/nova/virt/xenapi/vmops.py @@ -220,6 +220,9 @@ class VMOps(object): dh = SimpleDH() args = {'id': transaction_id, 'pub': str(dh.get_public())} resp = self._make_agent_call('key_init', instance, '', args) + if resp is None: + # No response from the agent + return resp_dict = json.loads(resp) # Successful return code from key_init is 'D0' if resp_dict['returncode'] != 'D0': @@ -232,6 +235,9 @@ class VMOps(object): # Send the encrypted password args['enc_pass'] = enc_pass resp = self._make_agent_call('password', instance, '', args) + if resp is None: + # No response from the agent + return resp_dict = json.loads(resp) # Successful return code from password is '0' if resp_dict['returncode'] != '0': @@ -384,12 +390,16 @@ class VMOps(object): task = self._session.async_call_plugin(plugin, method, args) ret = self._session.wait_for_task(instance_id, task) except self.XenAPI.Failure, e: + ret = None err_trace = e.details[-1] err_msg = err_trace.splitlines()[-1] + strargs = str(args) if 'TIMEOUT:' in err_msg: - raise exception.TimeoutException(err_msg) + LOG.error(_('TIMEOUT: The call to %(method)s timed out. ' + 'VM id=%(instance_id)s; args=%(strargs)s') % locals()) else: - raise RuntimeError("%s" % e.details[-1]) + LOG.error(_('The call to %(method)s returned an error: %(e)s. ' + 'VM id=%(instance_id)s; args=%(strargs)s') % locals()) return ret def add_to_xenstore(self, vm, path, key, value): -- cgit From c14425541a1e77eb2049b94060bc0c4cd1df578f Mon Sep 17 00:00:00 2001 From: Nachi Ueno Date: Wed, 12 Jan 2011 02:15:09 -0500 Subject: changed exception class --- nova/utils.py | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) (limited to 'nova') diff --git a/nova/utils.py b/nova/utils.py index afe7422d9..888ebfd81 100644 --- a/nova/utils.py +++ b/nova/utils.py @@ -219,10 +219,13 @@ def get_my_linklocal(interface): if address[0] is not None: return address[0] else: - return None - except RuntimeError as ex: - logging.warn(_("Couldn't get Link Local IP of %s :%s"), interface, ex) - return 'fe00::' + return 'fe00::' + except IndexError as ex: + logging.warn(_("Couldn't parse command from get Link Local IP of %s :%s"), interface, ex) + except ProcessExecutionError as ex: + logging.warn(_("Couldn't execute command to get Link Local IP of %s :%s"), interface, ex) + except: + return 'fe00::' def to_global_ipv6(prefix, mac): -- cgit From 1629dcf935a29c01d4e4ad509e33356daa93b051 Mon Sep 17 00:00:00 2001 From: Hisaharu Ishii Date: Wed, 12 Jan 2011 11:26:22 +0900 Subject: Fixed for pep8 Remove temporary debugging --- nova/db/api.py | 3 +++ nova/db/sqlalchemy/api.py | 4 ++++ nova/utils.py | 1 - 3 files changed, 7 insertions(+), 1 deletion(-) (limited to 'nova') diff --git a/nova/db/api.py b/nova/db/api.py index 8684a3aef..03e800466 100644 --- a/nova/db/api.py +++ b/nova/db/api.py @@ -284,6 +284,7 @@ def fixed_ip_get_instance(context, address): """Get an instance for a fixed ip by address.""" return IMPL.fixed_ip_get_instance(context, address) + def fixed_ip_get_instance_v6(context, address): return IMPL.fixed_ip_get_instance_v6(context, address) @@ -345,6 +346,7 @@ def instance_get_fixed_address(context, instance_id): """Get the fixed ip address of an instance.""" return IMPL.instance_get_fixed_address(context, instance_id) + def instance_get_fixed_address_v6(context, instance_id): return IMPL.instance_get_fixed_address_v6(context, instance_id) @@ -543,6 +545,7 @@ def project_get_network(context, project_id, associate=True): return IMPL.project_get_network(context, project_id) + def project_get_network_v6(context, project_id): return IMPL.project_get_network_v6(context, project_id) diff --git a/nova/db/sqlalchemy/api.py b/nova/db/sqlalchemy/api.py index ffc0ec221..7c3afa4ad 100644 --- a/nova/db/sqlalchemy/api.py +++ b/nova/db/sqlalchemy/api.py @@ -585,6 +585,7 @@ def fixed_ip_get_instance(context, address): fixed_ip_ref = fixed_ip_get_by_address(context, address) return fixed_ip_ref.instance + @require_context def fixed_ip_get_instance_v6(context, address): session = get_session() @@ -801,6 +802,7 @@ def instance_get_fixed_address(context, instance_id): return None return instance_ref.fixed_ip['address'] + @require_context def instance_get_fixed_address_v6(context, instance_id): session = get_session() @@ -811,6 +813,7 @@ def instance_get_fixed_address_v6(context, instance_id): mac = instance_ref.mac_address return utils.to_global_ipv6(prefix, mac) + @require_context def instance_get_floating_address(context, instance_id): session = get_session() @@ -1150,6 +1153,7 @@ def project_get_network(context, project_id, associate=True): first() return result + @require_context def project_get_network_v6(context, project_id): return project_get_network(context, project_id) diff --git a/nova/utils.py b/nova/utils.py index 888ebfd81..b08d5d620 100644 --- a/nova/utils.py +++ b/nova/utils.py @@ -47,7 +47,6 @@ TIME_FORMAT = "%Y-%m-%dT%H:%M:%SZ" def import_class(import_str): """Returns a class from a string including module and class""" mod_str, _sep, class_str = import_str.rpartition('.') - logging.debug(import_str) try: __import__(mod_str) return getattr(sys.modules[mod_str], class_str) -- cgit From d368d182d7fa4b0f0cd9c7c5ad1e804b19365b26 Mon Sep 17 00:00:00 2001 From: Nachi Ueno Date: Wed, 12 Jan 2011 12:05:27 +0900 Subject: Add DescribeInstanceV6 for backward compatibility --- nova/api/ec2/cloud.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) (limited to 'nova') diff --git a/nova/api/ec2/cloud.py b/nova/api/ec2/cloud.py index 8c925ffee..3d071782c 100644 --- a/nova/api/ec2/cloud.py +++ b/nova/api/ec2/cloud.py @@ -643,15 +643,21 @@ class CloudController(object): def describe_instances(self, context, **kwargs): return self._format_describe_instances(context) + def describe_instances_v6(self, context, **kwargs): + return self._format_describe_instances(context) + def _format_describe_instances(self, context): return {'reservationSet': self._format_instances(context)} + def _format_describe_instances_v6(self, context): + return {'reservationSet': self._format_instances(context,None,True)} + def _format_run_instances(self, context, reservation_id): i = self._format_instances(context, reservation_id) assert len(i) == 1 return i[0] - def _format_instances(self, context, reservation_id=None): + def _format_instances(self, context, reservation_id=None,use_v6=False): reservations = {} if reservation_id: instances = db.instance_get_all_by_reservation(context, @@ -677,7 +683,7 @@ class CloudController(object): if instance['fixed_ip']['floating_ips']: fixed = instance['fixed_ip'] floating_addr = fixed['floating_ips'][0]['address'] - if instance['fixed_ip']['network'] and FLAGS.use_ipv6: + if instance['fixed_ip']['network'] and use_v6: i['dnsNameV6'] = utils.to_global_ipv6( instance['fixed_ip']['network']['cidr_v6'], instance['mac_address']) -- cgit From 7a6b7c32ed25d1edc58b924ce5621dc0d8de9686 Mon Sep 17 00:00:00 2001 From: Nachi Ueno Date: Wed, 12 Jan 2011 03:50:09 -0500 Subject: Fixed bug --- nova/api/ec2/cloud.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'nova') diff --git a/nova/api/ec2/cloud.py b/nova/api/ec2/cloud.py index 3d071782c..d6f164b5c 100644 --- a/nova/api/ec2/cloud.py +++ b/nova/api/ec2/cloud.py @@ -644,7 +644,7 @@ class CloudController(object): return self._format_describe_instances(context) def describe_instances_v6(self, context, **kwargs): - return self._format_describe_instances(context) + return self._format_describe_instances_v6(context) def _format_describe_instances(self, context): return {'reservationSet': self._format_instances(context)} -- cgit From 04cd3241f442f1c6a9fd030ab47b4d15e79ec032 Mon Sep 17 00:00:00 2001 From: Hisaharu Ishii Date: Wed, 12 Jan 2011 18:37:18 +0900 Subject: Change command to get link local address Remove superfluous code --- nova/utils.py | 4 ++-- nova/virt/libvirt_conn.py | 1 - 2 files changed, 2 insertions(+), 3 deletions(-) (limited to 'nova') diff --git a/nova/utils.py b/nova/utils.py index 02bafc6c8..a13fa214c 100644 --- a/nova/utils.py +++ b/nova/utils.py @@ -212,8 +212,8 @@ def get_my_ip(): def get_my_linklocal(interface): try: - if_str = execute("ifconfig %s" % interface) - condition = "\s+inet6\s+addr:\s+([0-9a-f:]+/\d+)\s+Scope:Link" + if_str = execute("ip -f inet6 -o addr show %s" % interface) + condition = "\s+inet6\s+([0-9a-f:]+/\d+)\s+scope:link" links = [re.search(condition, x) for x in if_str[0].split('\n')] address = [w.group(1) for w in links if w is not None] if address[0] is not None: diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py index c6827edbe..e5f61f9aa 100644 --- a/nova/virt/libvirt_conn.py +++ b/nova/virt/libvirt_conn.py @@ -1024,7 +1024,6 @@ class NWFilterFirewall(FirewallDriver): security_group = db.security_group_get(context.get_admin_context(), security_group_id) rule_xml = "" - version = 4 v6protocol = {'tcp': 'tcp-ipv6', 'udp': 'udp-ipv6', 'icmp': 'icmpv6'} for rule in security_group.rules: rule_xml += "" -- cgit From b4600b088b61a5653be9a93a0497c9d80916c8c0 Mon Sep 17 00:00:00 2001 From: MORITA Kazutaka Date: Wed, 12 Jan 2011 21:12:25 +0900 Subject: Check whether 'device_path' has ':' before splitting it --- nova/virt/libvirt_conn.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'nova') diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py index 51c805c2f..1ea3b0aa4 100644 --- a/nova/virt/libvirt_conn.py +++ b/nova/virt/libvirt_conn.py @@ -224,14 +224,14 @@ class LibvirtConnection(object): virt_dom = self._conn.lookupByName(instance_name) mount_device = mountpoint.rpartition("/")[2] xml = '' - (protocol, vol_name) = device_path.split(':') if device_path.startswith('/dev/'): xml = """ """ % (device_path, mount_device) - elif vol_name != '': + elif device_path.find(':') >= 0: + (protocol, vol_name) = device_path.split(':', 1) xml = """ -- cgit From a6a2a057d8a027781e4270c9abc4f815c67293ec Mon Sep 17 00:00:00 2001 From: Nachi Ueno Date: Wed, 12 Jan 2011 10:12:18 -0500 Subject: Fixed syntax errors --- nova/api/ec2/cloud.py | 2 +- nova/utils.py | 2 +- nova/virt/libvirt_conn.py | 6 ++++-- 3 files changed, 6 insertions(+), 4 deletions(-) (limited to 'nova') diff --git a/nova/api/ec2/cloud.py b/nova/api/ec2/cloud.py index 72d7bcc95..c5c99f132 100644 --- a/nova/api/ec2/cloud.py +++ b/nova/api/ec2/cloud.py @@ -610,7 +610,7 @@ class CloudController(object): def describe_instances_v6(self, context, **kwargs): kwargs['use_v6'] = True - return self._format_describe_instances_v6(context, **kwargs) + return self._format_describe_instances(context, **kwargs) def _format_describe_instances(self, context, **kwargs): return {'reservationSet': self._format_instances(context, **kwargs)} diff --git a/nova/utils.py b/nova/utils.py index 49344699a..09c9a5f89 100644 --- a/nova/utils.py +++ b/nova/utils.py @@ -218,7 +218,7 @@ def get_my_ip(): def get_my_linklocal(interface): try: if_str = execute("ip -f inet6 -o addr show %s" % interface) - condition = "\s+inet6\s+([0-9a-f:]+/\d+)\s+scope:link" + condition = "\s+inet6\s+([0-9a-f:]+/\d+)\s+scope\s+link" links = [re.search(condition, x) for x in if_str[0].split('\n')] address = [w.group(1) for w in links if w is not None] if address[0] is not None: diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py index ec17e4db0..263138710 100644 --- a/nova/virt/libvirt_conn.py +++ b/nova/virt/libvirt_conn.py @@ -940,8 +940,8 @@ class NWFilterFirewall(FirewallDriver): ['no-mac-spoofing', 'no-ip-spoofing', 'no-arp-spoofing', - 'allow-dhcp-server', - 'allow-ra-server'])) + 'allow-dhcp-server' + ])) self._define_filter(self.nova_base_ipv4_filter) self._define_filter(self.nova_base_ipv6_filter) self._define_filter(self.nova_dhcp_filter) @@ -1035,6 +1035,8 @@ class NWFilterFirewall(FirewallDriver): instance_secgroup_filter_children = ['nova-base-ipv4', 'nova-base-ipv6', 'nova-allow-dhcp-server'] + if FLAGS.use_ipv6: + instance_secgroup_filter_children += ['nova-allow-ra-server'] ctxt = context.get_admin_context() -- cgit From b945fed7779bddf799aa4a180d44745052d2da8c Mon Sep 17 00:00:00 2001 From: Hisaharu Ishii Date: Wed, 12 Jan 2011 21:55:36 +0900 Subject: Support IPv6 firewall with IptablesFirewallDriver --- nova/db/sqlalchemy/api.py | 2 +- nova/virt/libvirt_conn.py | 66 ++++++++++++++++++++++++++++++++++++----------- 2 files changed, 52 insertions(+), 16 deletions(-) (limited to 'nova') diff --git a/nova/db/sqlalchemy/api.py b/nova/db/sqlalchemy/api.py index 3b3a88170..2ca16283f 100644 --- a/nova/db/sqlalchemy/api.py +++ b/nova/db/sqlalchemy/api.py @@ -798,7 +798,7 @@ def instance_get_fixed_address_v6(context, instance_id): session = get_session() with session.begin(): instance_ref = instance_get(context, instance_id, session=session) - network_ref = project_get_network(context, context.project_id) + network_ref = network_get_by_instance(context, instance_id) prefix = network_ref.cidr_v6 mac = instance_ref.mac_address return utils.to_global_ipv6(prefix, mac) diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py index 263138710..f2ffbf180 100644 --- a/nova/virt/libvirt_conn.py +++ b/nova/virt/libvirt_conn.py @@ -1131,11 +1131,17 @@ class IptablesFirewallDriver(FirewallDriver): def apply_ruleset(self): current_filter, _ = self.execute('sudo iptables-save -t filter') current_lines = current_filter.split('\n') - new_filter = self.modify_rules(current_lines) + new_filter = self.modify_rules(current_lines, 4) self.execute('sudo iptables-restore', process_input='\n'.join(new_filter)) + if(FLAGS.use_ipv6): + current_filter, _ = self.execute('sudo ip6tables-save -t filter') + current_lines = current_filter.split('\n') + new_filter = self.modify_rules(current_lines, 6) + self.execute('sudo ip6tables-restore', + process_input='\n'.join(new_filter)) - def modify_rules(self, current_lines): + def modify_rules(self, current_lines, ip_version): ctxt = context.get_admin_context() # Remove any trace of nova rules. new_filter = filter(lambda l: 'nova-' not in l, current_lines) @@ -1149,8 +1155,8 @@ class IptablesFirewallDriver(FirewallDriver): if not new_filter[rules_index].startswith(':'): break - our_chains = [':nova-ipv4-fallback - [0:0]'] - our_rules = ['-A nova-ipv4-fallback -j DROP'] + our_chains = [':nova-fallback - [0:0]'] + our_rules = ['-A nova-fallback -j DROP'] our_chains += [':nova-local - [0:0]'] our_rules += ['-A FORWARD -j nova-local'] @@ -1160,7 +1166,10 @@ class IptablesFirewallDriver(FirewallDriver): # First, we add instance chains and rules for instance in self.instances: chain_name = self._instance_chain_name(instance) - ip_address = self._ip_for_instance(instance) + if(ip_version == 4): + ip_address = self._ip_for_instance(instance) + elif(ip_version == 6): + ip_address = self._ip_for_instance_v6(instance) our_chains += [':%s - [0:0]' % chain_name] @@ -1186,13 +1195,19 @@ class IptablesFirewallDriver(FirewallDriver): our_rules += ['-A %s -j %s' % (chain_name, sg_chain_name)] - # Allow DHCP responses - dhcp_server = self._dhcp_server_for_instance(instance) - our_rules += ['-A %s -s %s -p udp --sport 67 --dport 68' % - (chain_name, dhcp_server)] + if(ip_version == 4): + # Allow DHCP responses + dhcp_server = self._dhcp_server_for_instance(instance) + our_rules += ['-A %s -s %s -p udp --sport 67 --dport 68' % + (chain_name, dhcp_server)] + elif(ip_version == 6): + # Allow RA responses + ra_server = self._ra_server_for_instance(instance) + our_rules += ['-A %s -s %s -p icmpv6' % + (chain_name, ra_server)] # If nothing matches, jump to the fallback chain - our_rules += ['-A %s -j nova-ipv4-fallback' % (chain_name,)] + our_rules += ['-A %s -j nova-fallback' % (chain_name,)] # then, security group chains and rules for security_group in security_groups: @@ -1205,15 +1220,22 @@ class IptablesFirewallDriver(FirewallDriver): for rule in rules: logging.info('%r', rule) - args = ['-A', chain_name, '-p', rule.protocol] - if rule.cidr: - args += ['-s', rule.cidr] - else: + if not rule.cidr: # Eventually, a mechanism to grant access for security # groups will turn up here. It'll use ipsets. continue + version = _get_ip_version(rule.cidr) + if version != ip_version: + continue + + protocol = rule.protocol + if version == 6 and rule.protocol == 'icmp': + protocol = 'icmpv6' + + args = ['-A', chain_name, '-p', protocol, '-s', rule.cidr] + if rule.protocol in ['udp', 'tcp']: if rule.from_port == rule.to_port: args += ['--dport', '%s' % (rule.from_port,)] @@ -1233,7 +1255,12 @@ class IptablesFirewallDriver(FirewallDriver): icmp_type_arg += '/%s' % icmp_code if icmp_type_arg: - args += ['-m', 'icmp', '--icmp-type', icmp_type_arg] + if(ip_version == 4): + args += ['-m', 'icmp', '--icmp-type', + icmp_type_arg] + elif(ip_version == 6): + args += ['-m', 'icmp6', '--icmpv6-type', + icmp_type_arg] args += ['-j ACCEPT'] our_rules += [' '.join(args)] @@ -1259,7 +1286,16 @@ class IptablesFirewallDriver(FirewallDriver): return db.instance_get_fixed_address(context.get_admin_context(), instance['id']) + def _ip_for_instance_v6(self, instance): + return db.instance_get_fixed_address_v6(context.get_admin_context(), + instance['id']) + def _dhcp_server_for_instance(self, instance): network = db.project_get_network(context.get_admin_context(), instance['project_id']) return network['gateway'] + + def _ra_server_for_instance(self, instance): + network = db.project_get_network(context.get_admin_context(), + instance['project_id']) + return network['ra_server'] -- cgit From df0be0318cf22d250bdf9abdd9ed3b91bb83f0ea Mon Sep 17 00:00:00 2001 From: Nachi Ueno Date: Thu, 13 Jan 2011 09:10:44 +0900 Subject: fixed method signature of modify_rules fixed unit_test for ipv6 --- nova/tests/test_network.py | 17 +++++++++-------- nova/virt/libvirt_conn.py | 2 +- 2 files changed, 10 insertions(+), 9 deletions(-) (limited to 'nova') diff --git a/nova/tests/test_network.py b/nova/tests/test_network.py index 407dbc769..00f9323f3 100644 --- a/nova/tests/test_network.py +++ b/nova/tests/test_network.py @@ -99,23 +99,24 @@ class NetworkTestCase(test.TestCase): def test_private_ipv6(self): """Make sure ipv6 is OK""" if FLAGS.use_ipv6: - instance_ref = self._create_instance(1) + instance_ref = self._create_instance(0) + address = self._create_address(0, instance_ref['id']) network_ref = db.project_get_network( - self.context, + context.get_admin_context(), self.context.project_id) address_v6 = db.instance_get_fixed_address_v6( - self.context, - instance_ref['id']) + context.get_admin_context(), + instance_ref['id']) self.assertEqual(instance_ref['mac_address'], utils.to_mac(address_v6)) instance_ref2 = db.fixed_ip_get_instance_v6( - self.context, - address_v6) + context.get_admin_context(), + address_v6) self.assertEqual(instance_ref['id'], instance_ref2['id']) self.assertEqual(address_v6, utils.to_global_ipv6( - network_ref['cidr_v6'], - instance_ref['mac_address'])) + network_ref['cidr_v6'], + instance_ref['mac_address'])) def test_public_network_association(self): """Makes sure that we can allocaate a public ip""" diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py index f2ffbf180..191292a9d 100644 --- a/nova/virt/libvirt_conn.py +++ b/nova/virt/libvirt_conn.py @@ -1141,7 +1141,7 @@ class IptablesFirewallDriver(FirewallDriver): self.execute('sudo ip6tables-restore', process_input='\n'.join(new_filter)) - def modify_rules(self, current_lines, ip_version): + def modify_rules(self, current_lines, ip_version=4): ctxt = context.get_admin_context() # Remove any trace of nova rules. new_filter = filter(lambda l: 'nova-' not in l, current_lines) -- cgit From a5026320b4ae14f0171ee450fe79ea687ab5647a Mon Sep 17 00:00:00 2001 From: Koji Iida Date: Thu, 13 Jan 2011 15:58:05 +0900 Subject: Fixed missing _(). Fixed to follow logging to LOG changes. Fixed merge miss (get_fixed_ip was moved away). Update some missing comments. --- nova/network/linux_net.py | 14 +++++++------- nova/tests/test_api.py | 1 - nova/utils.py | 17 ++--------------- 3 files changed, 9 insertions(+), 23 deletions(-) (limited to 'nova') diff --git a/nova/network/linux_net.py b/nova/network/linux_net.py index 50256db2d..677931603 100644 --- a/nova/network/linux_net.py +++ b/nova/network/linux_net.py @@ -292,12 +292,12 @@ interface %s """ % (network_ref['bridge'], network_ref['cidr_v6']) f.write(conf_str) - # Make sure dnsmasq can actually read it (it setuid()s to "nobody") + # Make sure radvd can actually read it (it setuid()s to "nobody") os.chmod(conffile, 0644) pid = _ra_pid_for(network_ref['bridge']) - # if dnsmasq is already running, then tell it to reload + # if radvd is already running, then tell it to reload if pid: out, _err = _execute('cat /proc/%d/cmdline' % pid, check_exit_code=False) @@ -306,9 +306,9 @@ interface %s _execute('sudo kill -HUP %d' % pid) return except Exception as exc: # pylint: disable-msg=W0703 - logging.debug("Hupping radvd threw %s", exc) + LOG.debug(_("Hupping radvd threw %s"), exc) else: - logging.debug("Pid %d is stale, relaunching radvd", pid) + LOG.debug(_("Pid %d is stale, relaunching radvd"), pid) command = _ra_cmd(network_ref) _execute(command) db.network_update(context, network_id, @@ -378,7 +378,7 @@ def _dnsmasq_cmd(net): def _ra_cmd(net): - """Builds dnsmasq command""" + """Builds radvd command""" cmd = ['sudo -E radvd', # ' -u nobody', ' -C %s' % _ra_file(net['bridge'], 'conf'), @@ -408,7 +408,7 @@ def _dhcp_file(bridge, kind): def _ra_file(bridge, kind): - """Return path to a pid, leases or conf file for a bridge""" + """Return path to a pid or conf file for a bridge""" if not os.path.exists(FLAGS.networks_path): os.makedirs(FLAGS.networks_path) @@ -433,7 +433,7 @@ def _dnsmasq_pid_for(bridge): def _ra_pid_for(bridge): - """Returns the pid for prior dnsmasq instance for a bridge + """Returns the pid for prior radvd instance for a bridge Returns None if no pid file exists diff --git a/nova/tests/test_api.py b/nova/tests/test_api.py index a508235c4..d22d7beb1 100644 --- a/nova/tests/test_api.py +++ b/nova/tests/test_api.py @@ -24,7 +24,6 @@ import httplib import random import StringIO import webob -import logging from nova import context from nova import flags diff --git a/nova/utils.py b/nova/utils.py index 09c9a5f89..27589c30c 100644 --- a/nova/utils.py +++ b/nova/utils.py @@ -202,19 +202,6 @@ def last_octet(address): return int(address.split(".")[-1]) -def get_my_ip(): - """Returns the actual ip of the local machine.""" - try: - csock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) - csock.connect(('8.8.8.8', 80)) - (addr, port) = csock.getsockname() - csock.close() - return addr - except socket.gaierror as ex: - logging.warn(_("Couldn't get IP, using 127.0.0.1 %s"), ex) - return "127.0.0.1" - - def get_my_linklocal(interface): try: if_str = execute("ip -f inet6 -o addr show %s" % interface) @@ -226,9 +213,9 @@ def get_my_linklocal(interface): else: return 'fe00::' except IndexError as ex: - logging.warn(_("Couldn't get Link Local IP of %s :%s"), interface, ex) + LOG.warn(_("Couldn't get Link Local IP of %s :%s"), interface, ex) except ProcessExecutionError as ex: - logging.warn(_("Couldn't get Link Local IP of %s :%s"), interface, ex) + LOG.warn(_("Couldn't get Link Local IP of %s :%s"), interface, ex) except: return 'fe00::' -- cgit From 1c694e9093c627bd50b35e9fb0ae11adf315a154 Mon Sep 17 00:00:00 2001 From: MORITA Kazutaka Date: Thu, 13 Jan 2011 20:59:02 +0900 Subject: Revert r510 and r512 because Josh had already done the same work --- nova/virt/libvirt_conn.py | 25 +++++-------------------- 1 file changed, 5 insertions(+), 20 deletions(-) (limited to 'nova') diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py index 1ea3b0aa4..00edfbdc8 100644 --- a/nova/virt/libvirt_conn.py +++ b/nova/virt/libvirt_conn.py @@ -223,26 +223,11 @@ class LibvirtConnection(object): def attach_volume(self, instance_name, device_path, mountpoint): virt_dom = self._conn.lookupByName(instance_name) mount_device = mountpoint.rpartition("/")[2] - xml = '' - if device_path.startswith('/dev/'): - xml = """ - - - - """ % (device_path, mount_device) - elif device_path.find(':') >= 0: - (protocol, vol_name) = device_path.split(':', 1) - xml = """ - - - - """ % (protocol, vol_name, mount_device) - else: - xml = """ - - - - """ % (device_path, mount_device) + xml = """ + + + + """ % (device_path, mount_device) virt_dom.attachDevice(xml) def _get_disk_xml(self, xml, device): -- cgit From 7af8f5ac5fc02abe79dec3cf3651b6f0a9deb78c Mon Sep 17 00:00:00 2001 From: Ed Leafe Date: Thu, 13 Jan 2011 10:51:31 -0600 Subject: Minor code cleanups --- nova/compute/manager.py | 2 +- nova/tests/test_xenapi.py | 4 ---- nova/virt/xenapi/vmops.py | 1 - 3 files changed, 1 insertion(+), 6 deletions(-) (limited to 'nova') diff --git a/nova/compute/manager.py b/nova/compute/manager.py index 267beca45..613ee45f6 100644 --- a/nova/compute/manager.py +++ b/nova/compute/manager.py @@ -125,7 +125,7 @@ class ComputeManager(manager.Manager): # FIXME(ja): include other fields from state? instance_ref = self.db.instance_get(context, instance_id) try: - info = self.driver.get_info(instance_ref) + info = self.driver.get_info(instance_ref['name']) state = info['state'] except exception.NotFound: state = power_state.NOSTATE diff --git a/nova/tests/test_xenapi.py b/nova/tests/test_xenapi.py index a03d616ad..93afe9ce1 100644 --- a/nova/tests/test_xenapi.py +++ b/nova/tests/test_xenapi.py @@ -265,10 +265,6 @@ class XenAPIVMTestCase(test.TestCase): return instance - - - - class XenAPIDiffieHellmanTestCase(test.TestCase): """ Unit tests for Diffie-Hellman code diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py index 6c6d25709..6e359ef82 100644 --- a/nova/virt/xenapi/vmops.py +++ b/nova/virt/xenapi/vmops.py @@ -22,7 +22,6 @@ Management class for VM-related functions (spawn, reboot, etc). import json import M2Crypto import os -import random import subprocess import tempfile import uuid -- cgit From 22b21cde84f200f6fd45ba5f2cfcb6a54e595f1b Mon Sep 17 00:00:00 2001 From: Ed Leafe Date: Thu, 13 Jan 2011 10:52:28 -0600 Subject: Minor code cleanups --- nova/tests/test_xenapi.py | 2 +- nova/virt/xenapi/vmops.py | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) (limited to 'nova') diff --git a/nova/tests/test_xenapi.py b/nova/tests/test_xenapi.py index 93afe9ce1..261ee0fde 100644 --- a/nova/tests/test_xenapi.py +++ b/nova/tests/test_xenapi.py @@ -286,6 +286,6 @@ class XenAPIDiffieHellmanTestCase(test.TestCase): enc = self.alice.encrypt(msg) dec = self.bob.decrypt(enc) self.assertEquals(dec, msg) - + def tearDown(self): super(XenAPIDiffieHellmanTestCase, self).tearDown() diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py index 6e359ef82..6c6d25709 100644 --- a/nova/virt/xenapi/vmops.py +++ b/nova/virt/xenapi/vmops.py @@ -22,6 +22,7 @@ Management class for VM-related functions (spawn, reboot, etc). import json import M2Crypto import os +import random import subprocess import tempfile import uuid -- cgit From 01a1ad3d2cdf61c73ca3ab7aa14e82f0e4450103 Mon Sep 17 00:00:00 2001 From: Ed Leafe Date: Thu, 13 Jan 2011 10:53:13 -0600 Subject: Minor code cleanups --- nova/virt/xenapi/vmops.py | 1 - 1 file changed, 1 deletion(-) (limited to 'nova') diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py index 6c6d25709..6e359ef82 100644 --- a/nova/virt/xenapi/vmops.py +++ b/nova/virt/xenapi/vmops.py @@ -22,7 +22,6 @@ Management class for VM-related functions (spawn, reboot, etc). import json import M2Crypto import os -import random import subprocess import tempfile import uuid -- cgit From bc0c5ba5026610013759fa731d21e2287e3d709a Mon Sep 17 00:00:00 2001 From: Nachi Ueno Date: Fri, 14 Jan 2011 06:25:41 +0900 Subject: Moved commands which needs sudo to nova.sh --- nova/network/linux_net.py | 5 ----- 1 file changed, 5 deletions(-) (limited to 'nova') diff --git a/nova/network/linux_net.py b/nova/network/linux_net.py index 677931603..891e9bc1d 100644 --- a/nova/network/linux_net.py +++ b/nova/network/linux_net.py @@ -124,11 +124,6 @@ def init_host(): (FLAGS.fixed_range, FLAGS.dmz_cidr)) _confirm_rule("POSTROUTING", "-t nat -s %(range)s -d %(range)s -j ACCEPT" % {'range': FLAGS.fixed_range}) - if(FLAGS.use_ipv6): - _execute('sudo bash -c ' + - '"echo 1 > /proc/sys/net/ipv6/conf/all/forwarding"') - _execute('sudo bash -c ' + - '"echo 0 > /proc/sys/net/ipv6/conf/all/accept_ra"') def bind_floating_ip(floating_ip, check_exit_code=True): -- cgit From cf0e5bd3eeb6b175b53df6ae0a0ef8957ec7ba13 Mon Sep 17 00:00:00 2001 From: Josh Kearney Date: Fri, 14 Jan 2011 11:24:45 -0600 Subject: Create and use a generic handler for RPC calls --- nova/compute/api.py | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) (limited to 'nova') diff --git a/nova/compute/api.py b/nova/compute/api.py index 90273da36..40b9e33e8 100644 --- a/nova/compute/api.py +++ b/nova/compute/api.py @@ -331,7 +331,7 @@ class API(base.Base): return self.db.instance_get_all(context) def _cast_compute_message(self, method, context, instance_id, host=None): - """Generic handler for RPC calls to compute.""" + """Generic handler for RPC casts to compute.""" if not host: instance = self.get(context, instance_id) host = instance['host'] @@ -339,6 +339,15 @@ class API(base.Base): kwargs = {'method': method, 'args': {'instance_id': instance_id}} rpc.cast(context, queue, kwargs) + def _call_compute_message(self, method, context, instance_id, host=None): + """Generic handler for RPC calls to compute.""" + if not host: + instance = self.get(context, instance_id) + host = instance["host"] + queue = self.db.queue_get_for(context, FLAGS.compute_topic, host) + kwargs = {"method": method, "args": {"instance_id": instance_id}} + return rpc.call(context, queue, kwargs) + def snapshot(self, context, instance_id, name): """Snapshot the given instance.""" self._cast_compute_message('snapshot_instance', context, instance_id) @@ -357,7 +366,10 @@ class API(base.Base): def get_diagnostics(self, context, instance_id): """Retrieve diagnostics for the given instance.""" - self._cast_compute_message('get_diagnostics', context, instance_id) + return self._call_compute_message( + "get_diagnostics", + context, + instance_id) def get_actions(self, context, instance_id): """Retrieve actions for the given instance.""" -- cgit From 76e875476848ee7f4aa483f65484903115e2bb49 Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya Date: Fri, 14 Jan 2011 10:25:44 -0800 Subject: import re, remove extra call in cloud.py. Move get_console_output to compute_api --- nova/api/ec2/cloud.py | 17 +---------------- nova/compute/api.py | 19 ++++++++++--------- 2 files changed, 11 insertions(+), 25 deletions(-) (limited to 'nova') diff --git a/nova/api/ec2/cloud.py b/nova/api/ec2/cloud.py index 630aaeaf2..4adcb7415 100644 --- a/nova/api/ec2/cloud.py +++ b/nova/api/ec2/cloud.py @@ -130,15 +130,6 @@ class CloudController(object): result[key] = [line] return result - def _trigger_refresh_security_group(self, context, security_group): - nodes = set([instance['host'] for instance in security_group.instances - if instance['host'] is not None]) - for node in nodes: - rpc.cast(context, - '%s.%s' % (FLAGS.compute_topic, node), - {"method": "refresh_security_group", - "args": {"security_group_id": security_group.id}}) - def _get_availability_zone_by_host(self, context, host): services = db.service_get_all_by_host(context, host) if len(services) > 0: @@ -522,13 +513,7 @@ class CloudController(object): # instance_id is passed in as a list of instances ec2_id = instance_id[0] instance_id = ec2_id_to_id(ec2_id) - instance_ref = self.compute_api.get(context, instance_id) - output = rpc.call(context, - '%s.%s' % (FLAGS.compute_topic, - instance_ref['host']), - {"method": "get_console_output", - "args": {"instance_id": instance_ref['id']}}) - + output = self.compute_api.get_console_output(context, instance_id) now = datetime.datetime.utcnow() return {"InstanceId": ec2_id, "Timestamp": now, diff --git a/nova/compute/api.py b/nova/compute/api.py index 90273da36..e6cfa459e 100644 --- a/nova/compute/api.py +++ b/nova/compute/api.py @@ -21,6 +21,7 @@ Handles all requests relating to instances (guest vms). """ import datetime +import re import time from nova import db @@ -385,23 +386,23 @@ class API(base.Base): def get_ajax_console(self, context, instance_id): """Get a url to an AJAX Console""" - instance = self.get(context, instance_id) - - output = rpc.call(context, - '%s.%s' % (FLAGS.compute_topic, - instance['host']), - {'method': 'get_ajax_console', - 'args': {'instance_id': instance['id']}}) - + output = self._call_compute_message('get_ajax_console', + context, + instance_id) rpc.cast(context, '%s' % FLAGS.ajax_console_proxy_topic, {'method': 'authorize_ajax_console', 'args': {'token': output['token'], 'host': output['host'], 'port': output['port']}}) - return {'url': '%s?token=%s' % (FLAGS.ajax_console_proxy_url, output['token'])} + def get_console_output(self, context, instance_id): + """Get console output for an an instance""" + return self._call_compute_message('get_console_output', + context, + instance_id) + def lock(self, context, instance_id): """lock the instance with instance_id""" self._cast_compute_message('lock_instance', context, instance_id) -- cgit From e0dcd52b98de4bfe9843b148decf22526713dea2 Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya Date: Fri, 14 Jan 2011 11:00:47 -0800 Subject: remove TrialTestCase again and fix merge issues --- nova/test.py | 99 +------------------------------------------ nova/tests/test_api.py | 4 +- nova/tests/test_log.py | 8 ++-- nova/tests/test_middleware.py | 2 +- nova/tests/test_twistd.py | 2 +- 5 files changed, 10 insertions(+), 105 deletions(-) (limited to 'nova') diff --git a/nova/test.py b/nova/test.py index 5922e4b1c..a26b85b04 100644 --- a/nova/test.py +++ b/nova/test.py @@ -23,8 +23,6 @@ and some black magic for inline callbacks. """ import datetime -import sys -import time import unittest import mox @@ -38,7 +36,6 @@ from nova import fakerabbit from nova import flags from nova import rpc from nova.network import manager as network_manager -from nova.tests import fake_flags FLAGS = flags.FLAGS @@ -74,7 +71,8 @@ class TestCase(unittest.TestCase): FLAGS.fixed_range, 5, 16, FLAGS.vlan_start, - FLAGS.vpn_start) + FLAGS.vpn_start, + FLAGS.fixed_range_v6) # emulate some of the mox stuff, we can't use the metaclass # because it screws with our generators @@ -139,96 +137,3 @@ class TestCase(unittest.TestCase): _wrapped.func_name = self.originalAttach.func_name rpc.Consumer.attach_to_eventlet = _wrapped - - -class TrialTestCase(trial_unittest.TestCase): - """Test case base class for all unit tests""" - def setUp(self): - """Run before each test method to initialize test environment""" - super(TrialTestCase, self).setUp() - # NOTE(vish): We need a better method for creating fixtures for tests - # now that we have some required db setup for the system - # to work properly. - self.start = datetime.datetime.utcnow() - ctxt = context.get_admin_context() - if db.network_count(ctxt) != 5: - network_manager.VlanManager().create_networks(ctxt, - FLAGS.fixed_range, - 5, 16, - FLAGS.vlan_start, - FLAGS.vpn_start, - FLAGS.fixed_range_v6) - - # emulate some of the mox stuff, we can't use the metaclass - # because it screws with our generators - self.mox = mox.Mox() - self.stubs = stubout.StubOutForTesting() - self.flag_overrides = {} - self.injected = [] - self._original_flags = FLAGS.FlagValuesDict() - - def tearDown(self): - """Runs after each test method to finalize/tear down test - environment.""" - try: - self.mox.UnsetStubs() - self.stubs.UnsetAll() - self.stubs.SmartUnsetAll() - self.mox.VerifyAll() - # NOTE(vish): Clean up any ips associated during the test. - ctxt = context.get_admin_context() - db.fixed_ip_disassociate_all_by_timeout(ctxt, FLAGS.host, - self.start) - db.network_disassociate_all(ctxt) - for x in self.injected: - try: - x.stop() - except AssertionError: - pass - - if FLAGS.fake_rabbit: - fakerabbit.reset_all() - - db.security_group_destroy_all(ctxt) - super(TrialTestCase, self).tearDown() - finally: - self.reset_flags() - - def flags(self, **kw): - """Override flag variables for a test""" - for k, v in kw.iteritems(): - if k in self.flag_overrides: - self.reset_flags() - raise Exception( - 'trying to override already overriden flag: %s' % k) - self.flag_overrides[k] = getattr(FLAGS, k) - setattr(FLAGS, k, v) - - def reset_flags(self): - """Resets all flag variables for the test. Runs after each test""" - FLAGS.Reset() - for k, v in self._original_flags.iteritems(): - setattr(FLAGS, k, v) - - def run(self, result=None): - test_method = getattr(self, self._testMethodName) - setattr(self, - self._testMethodName, - self._maybeInlineCallbacks(test_method, result)) - rv = super(TrialTestCase, self).run(result) - setattr(self, self._testMethodName, test_method) - return rv - - def _maybeInlineCallbacks(self, func, result): - def _wrapped(): - g = func() - if isinstance(g, defer.Deferred): - return g - if not hasattr(g, 'send'): - return defer.succeed(g) - - inlined = defer.inlineCallbacks(func) - d = inlined() - return d - _wrapped.func_name = func.func_name - return _wrapped diff --git a/nova/tests/test_api.py b/nova/tests/test_api.py index d22d7beb1..17789c25c 100644 --- a/nova/tests/test_api.py +++ b/nova/tests/test_api.py @@ -79,7 +79,7 @@ class FakeHttplibConnection(object): pass -class XmlConversionTestCase(test.TrialTestCase): +class XmlConversionTestCase(test.TestCase): """Unit test api xml conversion""" def test_number_conversion(self): conv = apirequest._try_convert @@ -96,7 +96,7 @@ class XmlConversionTestCase(test.TrialTestCase): self.assertEqual(conv('-0'), 0) -class ApiEc2TestCase(test.TrialTestCase): +class ApiEc2TestCase(test.TestCase): """Unit test for the cloud controller on an EC2 API""" def setUp(self): super(ApiEc2TestCase, self).setUp() diff --git a/nova/tests/test_log.py b/nova/tests/test_log.py index beb1d97cf..868a5ead3 100644 --- a/nova/tests/test_log.py +++ b/nova/tests/test_log.py @@ -9,7 +9,7 @@ def _fake_context(): return context.RequestContext(1, 1) -class RootLoggerTestCase(test.TrialTestCase): +class RootLoggerTestCase(test.TestCase): def setUp(self): super(RootLoggerTestCase, self).setUp() self.log = log.logging.root @@ -46,7 +46,7 @@ class RootLoggerTestCase(test.TrialTestCase): self.assert_(True) # didn't raise exception -class NovaFormatterTestCase(test.TrialTestCase): +class NovaFormatterTestCase(test.TestCase): def setUp(self): super(NovaFormatterTestCase, self).setUp() self.flags(logging_context_format_string="HAS CONTEXT "\ @@ -78,7 +78,7 @@ class NovaFormatterTestCase(test.TrialTestCase): self.assertEqual("NOCTXT: baz --DBG\n", self.stream.getvalue()) -class NovaLoggerTestCase(test.TrialTestCase): +class NovaLoggerTestCase(test.TestCase): def setUp(self): super(NovaLoggerTestCase, self).setUp() self.flags(default_log_levels=["nova-test=AUDIT"], verbose=False) @@ -96,7 +96,7 @@ class NovaLoggerTestCase(test.TrialTestCase): self.assertEqual(log.AUDIT, l.level) -class VerboseLoggerTestCase(test.TrialTestCase): +class VerboseLoggerTestCase(test.TestCase): def setUp(self): super(VerboseLoggerTestCase, self).setUp() self.flags(default_log_levels=["nova.test=AUDIT"], verbose=True) diff --git a/nova/tests/test_middleware.py b/nova/tests/test_middleware.py index 0febf52d6..9d49167ba 100644 --- a/nova/tests/test_middleware.py +++ b/nova/tests/test_middleware.py @@ -38,7 +38,7 @@ def conditional_forbid(req): return 'OK' -class LockoutTestCase(test.TrialTestCase): +class LockoutTestCase(test.TestCase): """Test case for the Lockout middleware.""" def setUp(self): # pylint: disable-msg=C0103 super(LockoutTestCase, self).setUp() diff --git a/nova/tests/test_twistd.py b/nova/tests/test_twistd.py index 75007b9c8..ff8627c3b 100644 --- a/nova/tests/test_twistd.py +++ b/nova/tests/test_twistd.py @@ -28,7 +28,7 @@ from nova import test FLAGS = flags.FLAGS -class TwistdTestCase(test.TrialTestCase): +class TwistdTestCase(test.TestCase): def setUp(self): super(TwistdTestCase, self).setUp() self.Options = twistd.WrapTwistedOptions(twistd.TwistdServerOptions) -- cgit From f16030423d43272c5c3bea7fe51a0e03f0d95846 Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya Date: Fri, 14 Jan 2011 11:19:51 -0800 Subject: undo accidental removal of fake_flags --- nova/test.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'nova') diff --git a/nova/test.py b/nova/test.py index a26b85b04..881baccd5 100644 --- a/nova/test.py +++ b/nova/test.py @@ -27,8 +27,6 @@ import unittest import mox import stubout -from twisted.internet import defer -from twisted.trial import unittest as trial_unittest from nova import context from nova import db @@ -36,6 +34,7 @@ from nova import fakerabbit from nova import flags from nova import rpc from nova.network import manager as network_manager +from nova.tests import fake_flags FLAGS = flags.FLAGS -- cgit From bf0d75e6f78bc3c66dce8481d44e52c40a9addb0 Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya Date: Fri, 14 Jan 2011 11:20:46 -0800 Subject: fix bad function signature in create_networks --- nova/network/manager.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'nova') diff --git a/nova/network/manager.py b/nova/network/manager.py index 4d553f074..a92bd3f99 100644 --- a/nova/network/manager.py +++ b/nova/network/manager.py @@ -239,8 +239,8 @@ class NetworkManager(manager.Manager): """Get the network host for the current context.""" raise NotImplementedError() - def create_networks(self, context, num_networks, network_size, cidr_v6, - *args, **kwargs): + def create_networks(self, context, cidr, num_networks, network_size, + cidr_v6, *args, **kwargs): """Create networks based on parameters.""" raise NotImplementedError() -- cgit From d0713a6a2149274eeeef6fd22e7da4706a8190ec Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya Date: Fri, 14 Jan 2011 11:36:48 -0800 Subject: removed rpc in cloud --- nova/api/ec2/cloud.py | 1 - 1 file changed, 1 deletion(-) (limited to 'nova') diff --git a/nova/api/ec2/cloud.py b/nova/api/ec2/cloud.py index 4adcb7415..fb7e6a59f 100644 --- a/nova/api/ec2/cloud.py +++ b/nova/api/ec2/cloud.py @@ -37,7 +37,6 @@ from nova import exception from nova import flags from nova import log as logging from nova import network -from nova import rpc from nova import utils from nova import volume from nova.compute import instance_types -- cgit