summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--nova/compute/api.py19
-rw-r--r--nova/compute/manager.py34
-rw-r--r--nova/tests/api/openstack/test_servers.py6
-rw-r--r--nova/virt/xenapi/vmops.py12
-rwxr-xr-xplugins/xenserver/xenapi/etc/xapi.d/plugins/agent3
-rwxr-xr-xplugins/xenserver/xenapi/etc/xapi.d/plugins/xenstore.py8
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__":