diff options
| author | Jenkins <jenkins@review.openstack.org> | 2012-10-30 15:37:50 +0000 |
|---|---|---|
| committer | Gerrit Code Review <review@openstack.org> | 2012-10-30 15:37:50 +0000 |
| commit | d4b40046edf16e8f5cbda374c1f744f678e320a9 (patch) | |
| tree | 499cc6021aa8aaf7f81e5233300381e8ddc41bc6 /nova/virt | |
| parent | 0882acd3644161086019407db818aa860b96f561 (diff) | |
| parent | f25c2d873014eb7d665e53dbb98ee80d9a424f6b (diff) | |
Merge "xenapi: refactor: Agent class"
Diffstat (limited to 'nova/virt')
| -rw-r--r-- | nova/virt/xenapi/agent.py | 221 | ||||
| -rw-r--r-- | nova/virt/xenapi/vmops.py | 27 |
2 files changed, 130 insertions, 118 deletions
diff --git a/nova/virt/xenapi/agent.py b/nova/virt/xenapi/agent.py index e3288a47b..0c17dccff 100644 --- a/nova/virt/xenapi/agent.py +++ b/nova/virt/xenapi/agent.py @@ -115,121 +115,128 @@ def _get_agent_version(session, instance, vm_ref): return resp['message'].replace('\\r\\n', '') -def get_agent_version(session, instance, vm_ref): - """Get the version of the agent running on the VM instance.""" +class XenAPIBasedAgent(object): + def __init__(self, session, instance, vm_ref): + self.session = session + self.instance = instance + self.vm_ref = vm_ref - LOG.debug(_('Querying agent version'), instance=instance) + def get_agent_version(self): + """Get the version of the agent running on the VM instance.""" - # The agent can be slow to start for a variety of reasons. On Windows, - # it will generally perform a setup process on first boot that can - # take a couple of minutes and then reboot. On Linux, the system can - # also take a while to boot. So we need to be more patient than - # normal as well as watch for domid changes + LOG.debug(_('Querying agent version'), instance=self.instance) - expiration = time.time() + FLAGS.agent_version_timeout - while time.time() < expiration: - ret = _get_agent_version(session, instance, vm_ref) - if ret: - return ret + # The agent can be slow to start for a variety of reasons. On Windows, + # it will generally perform a setup process on first boot that can + # take a couple of minutes and then reboot. On Linux, the system can + # also take a while to boot. So we need to be more patient than + # normal as well as watch for domid changes - LOG.info(_('Reached maximum time attempting to query agent version'), - instance=instance) + expiration = time.time() + FLAGS.agent_version_timeout + while time.time() < expiration: + ret = _get_agent_version(self.session, self.instance, self.vm_ref) + if ret: + return ret - return None + LOG.info(_('Reached maximum time attempting to query agent version'), + instance=self.instance) - -def agent_update(session, instance, vm_ref, agent_build): - """Update agent on the VM instance.""" - - LOG.info(_('Updating agent to %s'), agent_build['version'], - instance=instance) - - # Send the encrypted password - args = {'url': agent_build['url'], 'md5sum': agent_build['md5hash']} - resp = _call_agent(session, instance, vm_ref, 'agentupdate', args) - if resp['returncode'] != '0': - LOG.error(_('Failed to update agent: %(resp)r'), locals(), - instance=instance) - return None - return resp['message'] - - -def set_admin_password(session, instance, vm_ref, 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 a more advanced - library (such as M2Crypto) for compatibility with the agent code. - """ - LOG.debug(_('Setting admin password'), instance=instance) - - dh = SimpleDH() - - # Exchange keys - args = {'pub': str(dh.get_public())} - resp = _call_agent(session, instance, vm_ref, 'key_init', args) - - # Successful return code from key_init is 'D0' - if resp['returncode'] != 'D0': - msg = _('Failed to exchange keys: %(resp)r') % locals() - LOG.error(msg, instance=instance) - raise Exception(msg) - - # Some old versions of the Windows agent have a trailing \\r\\n - # (ie CRLF escaped) for some reason. Strip that off. - agent_pub = int(resp['message'].replace('\\r\\n', '')) - dh.compute_shared(agent_pub) - - # Some old versions of Linux and Windows agent expect trailing \n - # on password to work correctly. - enc_pass = dh.encrypt(new_pass + '\n') - - # Send the encrypted password - args = {'enc_pass': enc_pass} - resp = _call_agent(session, instance, vm_ref, 'password', args) - - # Successful return code from password is '0' - if resp['returncode'] != '0': - msg = _('Failed to update password: %(resp)r') % locals() - LOG.error(msg, instance=instance) - raise Exception(msg) - - return resp['message'] - - -def inject_file(session, instance, vm_ref, path, contents): - LOG.debug(_('Injecting file path: %r'), path, instance=instance) - - # Files/paths must be base64-encoded for transmission to agent - b64_path = base64.b64encode(path) - b64_contents = base64.b64encode(contents) - - args = {'b64_path': b64_path, 'b64_contents': b64_contents} - - # If the agent doesn't support file injection, a NotImplementedError - # will be raised with the appropriate message. - resp = _call_agent(session, instance, vm_ref, 'inject_file', args) - if resp['returncode'] != '0': - LOG.error(_('Failed to inject file: %(resp)r'), locals(), - instance=instance) - return None - - return resp['message'] - - -def resetnetwork(session, instance, vm_ref): - LOG.debug(_('Resetting network'), instance=instance) - - resp = _call_agent(session, instance, vm_ref, 'resetnetwork', - timeout=FLAGS.agent_resetnetwork_timeout) - if resp['returncode'] != '0': - LOG.error(_('Failed to reset network: %(resp)r'), locals(), - instance=instance) return None - return resp['message'] + def agent_update(self, agent_build): + """Update agent on the VM instance.""" + + LOG.info(_('Updating agent to %s'), agent_build['version'], + instance=self.instance) + + # Send the encrypted password + args = {'url': agent_build['url'], 'md5sum': agent_build['md5hash']} + resp = _call_agent( + self.session, self.instance, self.vm_ref, 'agentupdate', args) + if resp['returncode'] != '0': + LOG.error(_('Failed to update agent: %(resp)r'), locals(), + instance=self.instance) + return None + return resp['message'] + + def set_admin_password(self, 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 a more advanced + library (such as M2Crypto) for compatibility with the agent code. + """ + LOG.debug(_('Setting admin password'), instance=self.instance) + + dh = SimpleDH() + + # Exchange keys + args = {'pub': str(dh.get_public())} + resp = _call_agent( + self.session, self.instance, self.vm_ref, 'key_init', args) + + # Successful return code from key_init is 'D0' + if resp['returncode'] != 'D0': + msg = _('Failed to exchange keys: %(resp)r') % locals() + LOG.error(msg, instance=self.instance) + raise Exception(msg) + + # Some old versions of the Windows agent have a trailing \\r\\n + # (ie CRLF escaped) for some reason. Strip that off. + agent_pub = int(resp['message'].replace('\\r\\n', '')) + dh.compute_shared(agent_pub) + + # Some old versions of Linux and Windows agent expect trailing \n + # on password to work correctly. + enc_pass = dh.encrypt(new_pass + '\n') + + # Send the encrypted password + args = {'enc_pass': enc_pass} + resp = _call_agent( + self.session, self.instance, self.vm_ref, 'password', args) + + # Successful return code from password is '0' + if resp['returncode'] != '0': + msg = _('Failed to update password: %(resp)r') % locals() + LOG.error(msg, instance=self.instance) + raise Exception(msg) + + return resp['message'] + + def inject_file(self, path, contents): + LOG.debug(_('Injecting file path: %r'), path, instance=self.instance) + + # Files/paths must be base64-encoded for transmission to agent + b64_path = base64.b64encode(path) + b64_contents = base64.b64encode(contents) + + args = {'b64_path': b64_path, 'b64_contents': b64_contents} + + # If the agent doesn't support file injection, a NotImplementedError + # will be raised with the appropriate message. + resp = _call_agent( + self.session, self.instance, self.vm_ref, 'inject_file', args) + if resp['returncode'] != '0': + LOG.error(_('Failed to inject file: %(resp)r'), locals(), + instance=self.instance) + return None + + return resp['message'] + + def resetnetwork(self): + LOG.debug(_('Resetting network'), instance=self.instance) + + resp = _call_agent( + self.session, self.instance, self.vm_ref, 'resetnetwork', + timeout=FLAGS.agent_resetnetwork_timeout) + if resp['returncode'] != '0': + LOG.error(_('Failed to reset network: %(resp)r'), locals(), + instance=self.instance) + return None + + return resp['message'] def find_guest_agent(base_dir): diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py index 469892ae8..ac00be6d7 100644 --- a/nova/virt/xenapi/vmops.py +++ b/nova/virt/xenapi/vmops.py @@ -42,7 +42,7 @@ from nova.openstack.common import log as logging from nova.openstack.common import timeutils from nova import utils from nova.virt import firewall -from nova.virt.xenapi import agent +from nova.virt.xenapi import agent as xapi_agent from nova.virt.xenapi import pool_states from nova.virt.xenapi import vm_utils from nova.virt.xenapi import volume_utils @@ -156,6 +156,9 @@ class VMOps(object): self.vif_driver = vif_impl(xenapi_session=self._session) self.default_root_dev = '/dev/sda' + def _get_agent(self, instance, vm_ref): + return xapi_agent.XenAPIBasedAgent(self._session, instance, vm_ref) + def list_instances(self): """List VM instances.""" # TODO(justinsb): Should we just always use the details method? @@ -514,14 +517,15 @@ class VMOps(object): # Update agent, if necessary # This also waits until the agent starts - version = agent.get_agent_version(self._session, instance, vm_ref) + agent = self._get_agent(instance, vm_ref) + version = agent.get_agent_version() if version: LOG.info(_('Instance agent version: %s'), version, instance=instance) if (version and agent_build and cmp_version(version, agent_build['version']) < 0): - agent.agent_update(self._session, instance, vm_ref, agent_build) + agent.agent_update(agent_build) # if the guest agent is not available, configure the # instance, but skip the admin password configuration @@ -531,16 +535,14 @@ class VMOps(object): if injected_files: # Inject any files, if specified for path, contents in injected_files: - agent.inject_file(self._session, instance, vm_ref, - path, contents) + agent.inject_file(path, contents) # Set admin password, if necessary if admin_password and not no_agent: - agent.set_admin_password(self._session, instance, vm_ref, - admin_password) + agent.set_admin_password(admin_password) # Reset network config - agent.resetnetwork(self._session, instance, vm_ref) + agent.resetnetwork() # Set VCPU weight inst_type = db.instance_type_get(ctx, instance['instance_type_id']) @@ -834,12 +836,14 @@ class VMOps(object): def set_admin_password(self, instance, new_pass): """Set the root/admin password on the VM instance.""" vm_ref = self._get_vm_opaque_ref(instance) - agent.set_admin_password(self._session, instance, vm_ref, new_pass) + agent = self._get_agent(instance, vm_ref) + agent.set_admin_password(new_pass) def inject_file(self, instance, path, contents): """Write a file to the VM instance.""" vm_ref = self._get_vm_opaque_ref(instance) - agent.inject_file(self._session, instance, vm_ref, path, contents) + agent = self._get_agent(instance, vm_ref) + agent.inject_file(path, contents) @staticmethod def _sanitize_xenstore_key(key): @@ -1384,7 +1388,8 @@ class VMOps(object): def reset_network(self, instance): """Calls resetnetwork method in agent.""" vm_ref = self._get_vm_opaque_ref(instance) - agent.resetnetwork(self._session, instance, vm_ref) + agent = self._get_agent(instance, vm_ref) + agent.resetnetwork() def inject_hostname(self, instance, vm_ref, hostname): """Inject the hostname of the instance into the xenstore.""" |
