diff options
-rw-r--r-- | nova/compute/api.py | 19 | ||||
-rw-r--r-- | nova/compute/manager.py | 34 | ||||
-rw-r--r-- | nova/tests/api/openstack/test_servers.py | 6 | ||||
-rw-r--r-- | nova/virt/xenapi/vmops.py | 12 | ||||
-rwxr-xr-x | plugins/xenserver/xenapi/etc/xapi.d/plugins/agent | 3 | ||||
-rwxr-xr-x | plugins/xenserver/xenapi/etc/xapi.d/plugins/xenstore.py | 8 |
6 files changed, 56 insertions, 26 deletions
diff --git a/nova/compute/api.py b/nova/compute/api.py index be26d8ca3..63884be97 100644 --- a/nova/compute/api.py +++ b/nova/compute/api.py @@ -482,6 +482,17 @@ class API(base.Base): """Generic handler for RPC calls to the scheduler.""" rpc.cast(context, FLAGS.scheduler_topic, args) + def _find_host(self, context, instance_id): + """Find the host associated with an instance.""" + for attempts in xrange(10): + instance = self.get(context, instance_id) + host = instance["host"] + if host: + return host + time.sleep(1) + raise exception.Error(_("Unable to find host for Instance %s") + % instance_id) + def snapshot(self, context, instance_id, name): """Snapshot the given instance. @@ -635,8 +646,12 @@ class API(base.Base): def set_admin_password(self, context, instance_id, password=None): """Set the root/admin password for the given instance.""" - self._cast_compute_message( - 'set_admin_password', context, instance_id, password) + host = self._find_host(context, instance_id) + + rpc.cast(context, + self.db.queue_get_for(context, FLAGS.compute_topic, host), + {"method": "set_admin_password", + "args": {"instance_id": instance_id, "new_pass": password}}) def inject_file(self, context, instance_id): """Write a file to the given instance.""" diff --git a/nova/compute/manager.py b/nova/compute/manager.py index c6f957073..ae5b50ef3 100644 --- a/nova/compute/manager.py +++ b/nova/compute/manager.py @@ -40,6 +40,7 @@ import os import socket import sys import tempfile +import time import functools from eventlet import greenthread @@ -404,21 +405,30 @@ class ComputeManager(manager.SchedulerDependentManager): def set_admin_password(self, context, instance_id, new_pass=None): """Set the root/admin password for an instance on this host.""" context = context.elevated() - instance_ref = self.db.instance_get(context, instance_id) - instance_id = instance_ref['id'] - instance_state = instance_ref['state'] - expected_state = power_state.RUNNING - if instance_state != expected_state: - LOG.warn(_('trying to reset the password on a non-running ' - 'instance: %(instance_id)s (state: %(instance_state)s ' - 'expected: %(expected_state)s)') % locals()) - LOG.audit(_('instance %s: setting admin password'), - instance_ref['name']) + if new_pass is None: # Generate a random password new_pass = utils.generate_password(FLAGS.password_length) - self.driver.set_admin_password(instance_ref, new_pass) - self._update_state(context, instance_id) + + while True: + instance_ref = self.db.instance_get(context, instance_id) + instance_id = instance_ref["id"] + instance_state = instance_ref["state"] + expected_state = power_state.RUNNING + + if instance_state != expected_state: + time.sleep(5) + continue + else: + try: + self.driver.set_admin_password(instance_ref, new_pass) + LOG.audit(_("Instance %s: Root password set"), + instance_ref["name"]) + break + except Exception, e: + # Catch all here because this could be anything. + LOG.exception(e) + continue @exception.wrap_exception @checks_instance_lock diff --git a/nova/tests/api/openstack/test_servers.py b/nova/tests/api/openstack/test_servers.py index 5c643fcef..89edece42 100644 --- a/nova/tests/api/openstack/test_servers.py +++ b/nova/tests/api/openstack/test_servers.py @@ -134,6 +134,10 @@ def fake_compute_api(cls, req, id): return True +def find_host(self, context, instance_id): + return "nova" + + class ServersTest(test.TestCase): def setUp(self): @@ -473,6 +477,7 @@ class ServersTest(test.TestCase): "_get_kernel_ramdisk_from_image", kernel_ramdisk_mapping) self.stubs.Set(nova.api.openstack.common, "get_image_id_from_image_hash", image_id_from_hash) + self.stubs.Set(nova.compute.api.API, "_find_host", find_host) def _test_create_instance_helper(self): self._setup_for_create_instance() @@ -767,6 +772,7 @@ class ServersTest(test.TestCase): self.stubs.Set(nova.db.api, 'instance_update', server_update) + self.stubs.Set(nova.compute.api.API, "_find_host", find_host) req = webob.Request.blank('/v1.0/servers/1') req.method = 'PUT' diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py index 30f31517d..fe9a74dd6 100644 --- a/nova/virt/xenapi/vmops.py +++ b/nova/virt/xenapi/vmops.py @@ -428,11 +428,12 @@ class VMOps(object): """ # Need to uniquely identify this request. - transaction_id = str(uuid.uuid4()) + key_init_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) + key_init_args = {'id': key_init_transaction_id, + 'pub': str(dh.get_public())} + resp = self._make_agent_call('key_init', instance, '', key_init_args) if resp is None: # No response from the agent return @@ -446,8 +447,9 @@ class VMOps(object): 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) + password_transaction_id = str(uuid.uuid4()) + password_args = {'id': password_transaction_id, 'enc_pass': enc_pass} + resp = self._make_agent_call('password', instance, '', password_args) if resp is None: # No response from the agent return diff --git a/plugins/xenserver/xenapi/etc/xapi.d/plugins/agent b/plugins/xenserver/xenapi/etc/xapi.d/plugins/agent index 5496a6bd5..9e761f264 100755 --- a/plugins/xenserver/xenapi/etc/xapi.d/plugins/agent +++ b/plugins/xenserver/xenapi/etc/xapi.d/plugins/agent @@ -53,7 +53,6 @@ class TimeoutError(StandardError): pass -@jsonify def key_init(self, arg_dict): """Handles the Diffie-Hellman key exchange with the agent to establish the shared secret key used to encrypt/decrypt sensitive @@ -72,7 +71,6 @@ def key_init(self, arg_dict): return resp -@jsonify def password(self, arg_dict): """Writes a request to xenstore that tells the agent to set the root password for the given VM. The password should be @@ -80,7 +78,6 @@ def password(self, arg_dict): previous call to key_init. The encrypted password value should be passed as the value for the 'enc_pass' key in arg_dict. """ - pub = int(arg_dict["pub"]) enc_pass = arg_dict["enc_pass"] arg_dict["value"] = json.dumps({"name": "password", "value": enc_pass}) request_id = arg_dict["id"] diff --git a/plugins/xenserver/xenapi/etc/xapi.d/plugins/xenstore.py b/plugins/xenserver/xenapi/etc/xapi.d/plugins/xenstore.py index d33c7346b..6c589ed29 100755 --- a/plugins/xenserver/xenapi/etc/xapi.d/plugins/xenstore.py +++ b/plugins/xenserver/xenapi/etc/xapi.d/plugins/xenstore.py @@ -59,12 +59,12 @@ def read_record(self, arg_dict): cmd = ["xenstore-read", "/local/domain/%(dom_id)s/%(path)s" % arg_dict] try: ret, result = _run_command(cmd) - return result.rstrip("\n") + return result.strip() except pluginlib.PluginError, e: if arg_dict.get("ignore_missing_path", False): cmd = ["xenstore-exists", "/local/domain/%(dom_id)s/%(path)s" % arg_dict] - ret, result = _run_command(cmd).strip() + ret, result = _run_command(cmd) # If the path exists, the cmd should return "0" if ret != 0: # No such path, so ignore the error and return the @@ -171,7 +171,7 @@ def _paths_from_ls(recs): def _run_command(cmd): """Abstracts out the basics of issuing system commands. If the command returns anything in stderr, a PluginError is raised with that information. - Otherwise, the output from stdout is returned. + Otherwise, a tuple of (return code, stdout data) is returned. """ pipe = subprocess.PIPE proc = subprocess.Popen(cmd, stdin=pipe, stdout=pipe, stderr=pipe, @@ -180,7 +180,7 @@ def _run_command(cmd): err = proc.stderr.read() if err: raise pluginlib.PluginError(err) - return proc.stdout.read() + return (ret, proc.stdout.read()) if __name__ == "__main__": |