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/virt/fake.py | 12 ++++++++++ nova/virt/xenapi/vmops.py | 15 +++++++++--- nova/virt/xenapi_conn.py | 59 +++++++++++++++++++++++++---------------------- 3 files changed, 55 insertions(+), 31 deletions(-) (limited to 'nova/virt') 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/virt/xenapi/vmops.py | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) (limited to 'nova/virt') 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/virt') 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/virt') 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/virt/libvirt.qemu.xml.template | 1 + nova/virt/libvirt.uml.xml.template | 1 + nova/virt/libvirt_conn.py | 68 ++++++++++++++++++++++++++++++++----- 3 files changed, 61 insertions(+), 9 deletions(-) (limited to 'nova/virt') 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/virt/xenapi/vmops.py | 290 ++++++++++++++++++++++++++++++++++++++-------- nova/virt/xenapi_conn.py | 18 ++- 2 files changed, 254 insertions(+), 54 deletions(-) (limited to 'nova/virt') 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/virt') 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 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/virt/fake.py | 2 +- nova/virt/xenapi/vmops.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'nova/virt') 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/virt') 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/virt') 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/virt') 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/virt') 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/virt/libvirt.xml.template | 2 +- nova/virt/libvirt_conn.py | 16 +++++++++++----- 2 files changed, 12 insertions(+), 6 deletions(-) (limited to 'nova/virt') 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 1cc5e933ccc29a88d09d2050e5224ee27eda767c Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya Date: Wed, 5 Jan 2011 08:05:11 +0000 Subject: stop using partitions and first pass at cow images --- nova/virt/libvirt.xml.template | 6 +++ nova/virt/libvirt_conn.py | 110 ++++++++++++++++++++++++----------------- 2 files changed, 70 insertions(+), 46 deletions(-) (limited to 'nova/virt') diff --git a/nova/virt/libvirt.xml.template b/nova/virt/libvirt.xml.template index 3fb2243da..d6d9d3de6 100644 --- a/nova/virt/libvirt.xml.template +++ b/nova/virt/libvirt.xml.template @@ -58,6 +58,12 @@ + #if $getVar('local', False) + + + + + #end if #end if diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py index 00edfbdc8..883913926 100644 --- a/nova/virt/libvirt_conn.py +++ b/nova/virt/libvirt_conn.py @@ -85,6 +85,9 @@ flags.DEFINE_string('libvirt_uri', flags.DEFINE_bool('allow_project_net_traffic', True, 'Whether to allow in project network traffic') +flags.DEFINE_bool('use_cow_images', + True, + 'Whether to use cow images') def get_connection(read_only): @@ -418,19 +421,50 @@ class LibvirtConnection(object): return self._dump_file(fpath) + def _get_image(self, image_id, target, user, project, size=None): + if not os.path.exists(target): + if FLAGS.use_cow_images: + base = os.path.join(FLAGS.instances_path, '_base') + if not os.path.exists(base): + images.fetch(image_id, base, user, project) + if size: + # TODO(vish): Attempt to resize the filesystem + disk.extend(base, size) + utils.execute('qemu-img create -f qcow2 -o ' + 'cluster_size=2M,backing_file=%s %s' + % (base, target)) + else: + images.fetch(image_id, target, user, project) + if size: + # TODO(vish): Attempt to resize the filesystem + disk.extend(target, size) + + def _get_local(self, local_gb, target): + if not os.path.exists(target): + last_mb = local_gb * 1024 - 1 + if FLAGS.use_cow_images: + base = os.path.join(FLAGS.instances_path, '_base') + if not os.path.exists(base): + utils.execute('dd if=/dev/zero of=%s bs=1M count=1' + 'seek=%s' % (base, last_mb)) + utils.execute('qemu-img create -f qcow2 -o ' + 'cluster_size=2M,backing_file=%s %s' + % (base, target)) + else: + utils.execute('dd if=/dev/zero of=%s bs=1M count=1' + 'seek=%s' % (base, last_mb)) + def _create_image(self, inst, libvirt_xml, prefix='', disk_images=None): # syntactic nicety - basepath = lambda fname = '', prefix = prefix: os.path.join( - FLAGS.instances_path, - inst['name'], - prefix + fname) + def basepath(fname='', prefix=prefix): + return os.path.join(FLAGS.instances_path, + inst['name'], + prefix + fname) # ensure directories exist and are writable utils.execute('mkdir -p %s' % basepath(prefix='')) utils.execute('chmod 0777 %s' % basepath(prefix='')) - # TODO(termie): these are blocking calls, it would be great - # if they weren't. logging.info(_('instance %s: Creating image'), inst['name']) f = open(basepath('libvirt.xml'), 'w') f.write(libvirt_xml) @@ -447,23 +481,26 @@ class LibvirtConnection(object): disk_images = {'image_id': inst['image_id'], 'kernel_id': inst['kernel_id'], 'ramdisk_id': inst['ramdisk_id']} - if not os.path.exists(basepath('disk')): - images.fetch(inst.image_id, basepath('disk-raw'), user, - project) - - if inst['kernel_id']: - if not os.path.exists(basepath('kernel')): - images.fetch(inst['kernel_id'], basepath('kernel'), - user, project) - if inst['ramdisk_id']: - if not os.path.exists(basepath('ramdisk')): - images.fetch(inst['ramdisk_id'], basepath('ramdisk'), - user, project) - - def execute(cmd, process_input=None, check_exit_code=True): - return utils.execute(cmd=cmd, - process_input=process_input, - check_exit_code=check_exit_code) + + if disk_images['kernel_id']: + self._get_image(disk_images['kernel_id'], basepath('kernel'), + user, project) + if disk_images['ramdisk_id']: + self._get_image(disk_images['ramdisk_id'], basepath('ramdisk'), + user, project) + + + size = FLAGS.minimum_root_size + if not FLAGS.use_cow_images: + if inst['instance_type'] == 'm1.tiny' or prefix == 'rescue-': + size = None + + self._get_image(disk_images['image_id'], basepath('disk'), + user, project, size) + + type_data = instance_types.INSTANCE_TYPES[inst['instance_type']] + self._get_local(type_data['local_gb'], basepath('local'), + user, project, size) # For now, we assume that if we're not using a kernel, we're using a # partitioned disk image where the target partition is the first @@ -493,34 +530,14 @@ class LibvirtConnection(object): logging.info(_('instance %s: injecting net into image %s'), inst['name'], inst.image_id) try: - disk.inject_data(basepath('disk-raw'), key, net, - partition=target_partition, - execute=execute) + disk.inject_data(basepath('disk'), key, net, + partition=target_partition) except Exception as e: # This could be a windows image, or a vmdk format disk logging.warn(_('instance %s: ignoring error injecting data' ' into image %s (%s)'), inst['name'], inst.image_id, e) - if inst['kernel_id']: - if os.path.exists(basepath('disk')): - utils.execute('rm -f %s' % basepath('disk')) - - local_bytes = (instance_types.INSTANCE_TYPES[inst.instance_type] - ['local_gb'] - * 1024 * 1024 * 1024) - - resize = True - if inst['instance_type'] == 'm1.tiny' or prefix == 'rescue-': - resize = False - - if inst['kernel_id']: - disk.partition(basepath('disk-raw'), basepath('disk'), - local_bytes, resize, execute=execute) - else: - os.rename(basepath('disk-raw'), basepath('disk')) - disk.extend(basepath('disk'), local_bytes, execute=execute) - if FLAGS.libvirt_type == 'uml': utils.execute('sudo chown root %s' % basepath('disk')) @@ -558,7 +575,8 @@ class LibvirtConnection(object): 'ip_address': ip_address, 'dhcp_server': dhcp_server, 'extra_params': extra_params, - 'rescue': rescue} + 'rescue': rescue, + 'local': instance_type['local_gb']} if not rescue: if instance['kernel_id']: xml_info['kernel'] = xml_info['basepath'] + "/kernel" -- 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/virt') 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/virt') 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/virt') 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/virt') 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/virt') 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 b5f8ab0e913c121a80ff0efe358960099e7c87f8 Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya Date: Wed, 5 Jan 2011 19:16:17 +0000 Subject: fix injection and xml --- nova/virt/libvirt.xml.template | 10 ++-- nova/virt/libvirt_conn.py | 109 ++++++++++++++++++++++++++++------------- 2 files changed, 81 insertions(+), 38 deletions(-) (limited to 'nova/virt') diff --git a/nova/virt/libvirt.xml.template b/nova/virt/libvirt.xml.template index d6d9d3de6..995d8f469 100644 --- a/nova/virt/libvirt.xml.template +++ b/nova/virt/libvirt.xml.template @@ -7,13 +7,13 @@ #set $disk_bus = 'uml' uml /usr/bin/linux - /dev/ubda1 + /dev/ubda #else #if $type == 'xen' #set $disk_prefix = 'sd' #set $disk_bus = 'scsi' linux - /dev/xvda1 + /dev/xvda #else #set $disk_prefix = 'vd' #set $disk_bus = 'virtio' @@ -28,7 +28,7 @@ #if $type == 'xen' ro #else - root=/dev/vda1 console=ttyS0 + root=/dev/vda console=ttyS0 #end if #if $getVar('ramdisk', None) ${ramdisk} @@ -46,20 +46,24 @@ #if $getVar('rescue', False) + + #else + #if $getVar('local', False) + diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py index 883913926..ae725b766 100644 --- a/nova/virt/libvirt_conn.py +++ b/nova/virt/libvirt_conn.py @@ -118,6 +118,32 @@ def _get_net_and_mask(cidr): return str(net.net()), str(net.netmask()) +def wrap_cow_image(key): + def _decorator(retrieve_fn): + def _wrap(*args, **kwargs): + target = kwargs['target'] + if not os.path.exists(target): + if FLAGS.use_cow_images: + base_dir = os.path.join(FLAGS.instances_path, '_base') + if not os.path.exists(base_dir): + os.mkdir(base_dir) + os.chmod(base_dir, 0777) + base = os.path.join(base_dir, str(kwargs[key])) + if not os.path.exists(base): + kwargs['target'] = base + retrieve_fn(*args, **kwargs) + if kwargs.get('cow'): + utils.execute('qemu-img create -f qcow2 -o ' + 'cluster_size=2M,backing_file=%s %s' + % (base, target)) + else: + utils.execute('cp %s %s' % (base, target)) + else: + retrieve_fn(*args, **kwargs) + _wrap.func_name = retrieve_fn.func_name + return _wrap + return _decorator + class LibvirtConnection(object): def __init__(self, read_only): @@ -421,38 +447,36 @@ class LibvirtConnection(object): return self._dump_file(fpath) - def _get_image(self, image_id, target, user, project, size=None): - if not os.path.exists(target): - if FLAGS.use_cow_images: - base = os.path.join(FLAGS.instances_path, '_base') - if not os.path.exists(base): - images.fetch(image_id, base, user, project) - if size: - # TODO(vish): Attempt to resize the filesystem - disk.extend(base, size) - utils.execute('qemu-img create -f qcow2 -o ' - 'cluster_size=2M,backing_file=%s %s' - % (base, target)) - else: - images.fetch(image_id, target, user, project) - if size: - # TODO(vish): Attempt to resize the filesystem - disk.extend(target, size) - - def _get_local(self, local_gb, target): + def _get_image(self, retrieve_fn, key, target, *args, **kwargs): if not os.path.exists(target): - last_mb = local_gb * 1024 - 1 if FLAGS.use_cow_images: - base = os.path.join(FLAGS.instances_path, '_base') + base_dir = os.path.join(FLAGS.instances_path, '_base') + if not os.path.exists(base_dir): + os.mkdir(base_dir) + os.chmod(base_dir, 0777) + base = os.path.join(base_dir, kwargs[key]) if not os.path.exists(base): - utils.execute('dd if=/dev/zero of=%s bs=1M count=1' - 'seek=%s' % (base, last_mb)) + retrieve_fn(target=base, *args, **kwargs) utils.execute('qemu-img create -f qcow2 -o ' 'cluster_size=2M,backing_file=%s %s' % (base, target)) else: - utils.execute('dd if=/dev/zero of=%s bs=1M count=1' - 'seek=%s' % (base, last_mb)) + retrieve_fn(target=base, *args, **kwargs) + + @wrap_cow_image('image_id') + def _fetch_image(self, target, image_id, user, project, + size=None, **kwargs): + images.fetch(image_id, target, user, project) + # TODO(vish): resize filesystem + if size: + disk.extend(target, size) + + @wrap_cow_image('local_gb') + def _create_local(self, target, local_gb): + last_mb = local_gb * 1024 - 1 + utils.execute('dd if=/dev/zero of=%s bs=1M count=1' + 'seek=%s' % (target, last_mb)) + # TODO(vish): format disk def _create_image(self, inst, libvirt_xml, prefix='', disk_images=None): # syntactic nicety @@ -483,11 +507,15 @@ class LibvirtConnection(object): 'ramdisk_id': inst['ramdisk_id']} if disk_images['kernel_id']: - self._get_image(disk_images['kernel_id'], basepath('kernel'), - user, project) + self._fetch_image(target=basepath('kernel'), + image_id=disk_images['kernel_id'], + user=user, + project=project) if disk_images['ramdisk_id']: - self._get_image(disk_images['ramdisk_id'], basepath('ramdisk'), - user, project) + self._fetch_image(target=basepath('ramdisk'), + image_id=disk_images['ramdisk_id'], + user=user, + project=project) size = FLAGS.minimum_root_size @@ -495,12 +523,17 @@ class LibvirtConnection(object): if inst['instance_type'] == 'm1.tiny' or prefix == 'rescue-': size = None - self._get_image(disk_images['image_id'], basepath('disk'), - user, project, size) - + self._fetch_image(target=basepath('disk'), + image_id=disk_images['image_id'], + user=user, + project=project, + size=size, + cow=True) type_data = instance_types.INSTANCE_TYPES[inst['instance_type']] - self._get_local(type_data['local_gb'], basepath('local'), - user, project, size) + + if type_data['local_gb']: + self._create_local(target=basepath('local'), + local_gb=type_data['local_gb']) # For now, we assume that if we're not using a kernel, we're using a # partitioned disk image where the target partition is the first @@ -534,6 +567,7 @@ class LibvirtConnection(object): partition=target_partition) except Exception as e: # This could be a windows image, or a vmdk format disk + logging.exception('test') logging.warn(_('instance %s: ignoring error injecting data' ' into image %s (%s)'), inst['name'], inst.image_id, e) @@ -563,6 +597,10 @@ class LibvirtConnection(object): "value=\"%s\" />\n") % (net, mask) else: extra_params = "\n" + if FLAGS.use_cow_images: + driver_type = 'qcow2' + else: + driver_type = 'raw' xml_info = {'type': FLAGS.libvirt_type, 'name': instance['name'], @@ -576,7 +614,8 @@ class LibvirtConnection(object): 'dhcp_server': dhcp_server, 'extra_params': extra_params, 'rescue': rescue, - 'local': instance_type['local_gb']} + 'local': instance_type['local_gb'], + 'driver_type': driver_type} if not rescue: if instance['kernel_id']: xml_info['kernel'] = xml_info['basepath'] + "/kernel" -- cgit From f1f292a787ba20134c007da087bd9585d1875e86 Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya Date: Wed, 5 Jan 2011 19:50:39 +0000 Subject: more fixes, docstrings --- nova/virt/libvirt_conn.py | 83 +++++++++++++++++++++++------------------------ 1 file changed, 40 insertions(+), 43 deletions(-) (limited to 'nova/virt') diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py index ae725b766..95a374603 100644 --- a/nova/virt/libvirt_conn.py +++ b/nova/virt/libvirt_conn.py @@ -118,28 +118,38 @@ def _get_net_and_mask(cidr): return str(net.net()), str(net.netmask()) -def wrap_cow_image(key): +def wrap_image_cache(key): + """Decorator for a method that creates an image to store it in cache. + + This wrapper will save the image into a common store and create a + copy for use by the hypervisor. + + The underlying method should be called with kwargs and specify + a kwarg of target representing where the image will be saved. The + key argument to the wrapper defines which kwarg of the underlying + method to use as the filename of the base image. The filename needs + to be unique to a given image. + + If kwargs['cow'] is True, it will make a CoW image instead of a copy. + """ def _decorator(retrieve_fn): def _wrap(*args, **kwargs): target = kwargs['target'] if not os.path.exists(target): - if FLAGS.use_cow_images: - base_dir = os.path.join(FLAGS.instances_path, '_base') - if not os.path.exists(base_dir): - os.mkdir(base_dir) - os.chmod(base_dir, 0777) - base = os.path.join(base_dir, str(kwargs[key])) - if not os.path.exists(base): - kwargs['target'] = base - retrieve_fn(*args, **kwargs) - if kwargs.get('cow'): - utils.execute('qemu-img create -f qcow2 -o ' - 'cluster_size=2M,backing_file=%s %s' - % (base, target)) - else: - utils.execute('cp %s %s' % (base, target)) - else: + base_dir = os.path.join(FLAGS.instances_path, '_base') + if not os.path.exists(base_dir): + os.mkdir(base_dir) + os.chmod(base_dir, 0777) + base = os.path.join(base_dir, str(kwargs[key])) + if not os.path.exists(base): + kwargs['target'] = base retrieve_fn(*args, **kwargs) + if kwargs.get('cow'): + utils.execute('qemu-img create -f qcow2 -o ' + 'cluster_size=2M,backing_file=%s %s' + % (base, target)) + else: + utils.execute('cp %s %s' % (base, target)) _wrap.func_name = retrieve_fn.func_name return _wrap return _decorator @@ -447,36 +457,21 @@ class LibvirtConnection(object): return self._dump_file(fpath) - def _get_image(self, retrieve_fn, key, target, *args, **kwargs): - if not os.path.exists(target): - if FLAGS.use_cow_images: - base_dir = os.path.join(FLAGS.instances_path, '_base') - if not os.path.exists(base_dir): - os.mkdir(base_dir) - os.chmod(base_dir, 0777) - base = os.path.join(base_dir, kwargs[key]) - if not os.path.exists(base): - retrieve_fn(target=base, *args, **kwargs) - utils.execute('qemu-img create -f qcow2 -o ' - 'cluster_size=2M,backing_file=%s %s' - % (base, target)) - else: - retrieve_fn(target=base, *args, **kwargs) - - @wrap_cow_image('image_id') + @wrap_image_cache('image_id') def _fetch_image(self, target, image_id, user, project, - size=None, **kwargs): + size=None, cow=False): + """Grab image and optionally attempt to resize it""" images.fetch(image_id, target, user, project) - # TODO(vish): resize filesystem if size: disk.extend(target, size) - @wrap_cow_image('local_gb') - def _create_local(self, target, local_gb): + @wrap_image_cache('local_gb') + def _create_local(self, target, local_gb, cow=False): + """Create a blank image of specified size""" last_mb = local_gb * 1024 - 1 - utils.execute('dd if=/dev/zero of=%s bs=1M count=1' + utils.execute('dd if=/dev/zero of=%s bs=1M count=1 ' 'seek=%s' % (target, last_mb)) - # TODO(vish): format disk + # TODO(vish): should we format disk by default? def _create_image(self, inst, libvirt_xml, prefix='', disk_images=None): # syntactic nicety @@ -528,12 +523,13 @@ class LibvirtConnection(object): user=user, project=project, size=size, - cow=True) + cow=FLAGS.use_cow_images) type_data = instance_types.INSTANCE_TYPES[inst['instance_type']] if type_data['local_gb']: self._create_local(target=basepath('local'), - local_gb=type_data['local_gb']) + local_gb=type_data['local_gb'], + cow=FLAGS.use_cow_images) # For now, we assume that if we're not using a kernel, we're using a # partitioned disk image where the target partition is the first @@ -564,7 +560,8 @@ class LibvirtConnection(object): inst['name'], inst.image_id) try: disk.inject_data(basepath('disk'), key, net, - partition=target_partition) + partition=target_partition, + nbd=FLAGS.use_cow_images) except Exception as e: # This could be a windows image, or a vmdk format disk logging.exception('test') -- cgit From 3d30bb1706812c4e6f9c1e01b373bb076a9f7ee3 Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya Date: Wed, 5 Jan 2011 19:52:55 +0000 Subject: pep8 cleanup --- nova/virt/libvirt_conn.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'nova/virt') diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py index 95a374603..77ff281d5 100644 --- a/nova/virt/libvirt_conn.py +++ b/nova/virt/libvirt_conn.py @@ -154,6 +154,7 @@ def wrap_image_cache(key): return _wrap return _decorator + class LibvirtConnection(object): def __init__(self, read_only): @@ -512,7 +513,6 @@ class LibvirtConnection(object): user=user, project=project) - size = FLAGS.minimum_root_size if not FLAGS.use_cow_images: if inst['instance_type'] == 'm1.tiny' or prefix == 'rescue-': -- cgit From a6b82b3015a64922a0733bd0dd5463b1a49ca080 Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya Date: Wed, 5 Jan 2011 20:46:18 +0000 Subject: simplify decorator into a wrapper fn --- nova/virt/libvirt_conn.py | 106 +++++++++++++++++++++++----------------------- 1 file changed, 52 insertions(+), 54 deletions(-) (limited to 'nova/virt') diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py index 77ff281d5..f2803ab55 100644 --- a/nova/virt/libvirt_conn.py +++ b/nova/virt/libvirt_conn.py @@ -118,43 +118,6 @@ def _get_net_and_mask(cidr): return str(net.net()), str(net.netmask()) -def wrap_image_cache(key): - """Decorator for a method that creates an image to store it in cache. - - This wrapper will save the image into a common store and create a - copy for use by the hypervisor. - - The underlying method should be called with kwargs and specify - a kwarg of target representing where the image will be saved. The - key argument to the wrapper defines which kwarg of the underlying - method to use as the filename of the base image. The filename needs - to be unique to a given image. - - If kwargs['cow'] is True, it will make a CoW image instead of a copy. - """ - def _decorator(retrieve_fn): - def _wrap(*args, **kwargs): - target = kwargs['target'] - if not os.path.exists(target): - base_dir = os.path.join(FLAGS.instances_path, '_base') - if not os.path.exists(base_dir): - os.mkdir(base_dir) - os.chmod(base_dir, 0777) - base = os.path.join(base_dir, str(kwargs[key])) - if not os.path.exists(base): - kwargs['target'] = base - retrieve_fn(*args, **kwargs) - if kwargs.get('cow'): - utils.execute('qemu-img create -f qcow2 -o ' - 'cluster_size=2M,backing_file=%s %s' - % (base, target)) - else: - utils.execute('cp %s %s' % (base, target)) - _wrap.func_name = retrieve_fn.func_name - return _wrap - return _decorator - - class LibvirtConnection(object): def __init__(self, read_only): @@ -458,16 +421,42 @@ class LibvirtConnection(object): return self._dump_file(fpath) - @wrap_image_cache('image_id') - def _fetch_image(self, target, image_id, user, project, - size=None, cow=False): + def _cache_image(self, fn, target, fname, cow=False, *args, **kwargs): + """Wrapper to cache a method that creates an image. + + This wrapper will save the image into a common store and create a + copy for use by the hypervisor. + + The underlying method should specify a kwarg of target representing + where the image will be saved. + + fname is used as the filename of the base image. The filename needs + to be fname to a given image. + + If cow is True, it will make a CoW image instead of a copy. + """ + if not os.path.exists(target): + base_dir = os.path.join(FLAGS.instances_path, '_base') + if not os.path.exists(base_dir): + os.mkdir(base_dir) + os.chmod(base_dir, 0777) + base = os.path.join(base_dir, fname) + if not os.path.exists(base): + fn(target=base, *args, **kwargs) + if cow: + utils.execute('qemu-img create -f qcow2 -o ' + 'cluster_size=2M,backing_file=%s %s' + % (base, target)) + else: + utils.execute('cp %s %s' % (base, target)) + + def _fetch_image(self, target, image_id, user, project, size=None): """Grab image and optionally attempt to resize it""" images.fetch(image_id, target, user, project) if size: disk.extend(target, size) - @wrap_image_cache('local_gb') - def _create_local(self, target, local_gb, cow=False): + def _create_local(self, target, local_gb): """Create a blank image of specified size""" last_mb = local_gb * 1024 - 1 utils.execute('dd if=/dev/zero of=%s bs=1M count=1 ' @@ -503,33 +492,42 @@ class LibvirtConnection(object): 'ramdisk_id': inst['ramdisk_id']} if disk_images['kernel_id']: - self._fetch_image(target=basepath('kernel'), + self._cache_image(fn=self._fetch_image, + target=basepath('kernel'), + fname=disk_images['kernel_id'], image_id=disk_images['kernel_id'], user=user, project=project) if disk_images['ramdisk_id']: - self._fetch_image(target=basepath('ramdisk'), + self._cache_image(fn=self._fetch_image, + target=basepath('ramdisk'), + fname=disk_images['ramdisk_id'], image_id=disk_images['ramdisk_id'], user=user, project=project) + root_fname = disk_images['image_id'] size = FLAGS.minimum_root_size - if not FLAGS.use_cow_images: - if inst['instance_type'] == 'm1.tiny' or prefix == 'rescue-': - size = None - - self._fetch_image(target=basepath('disk'), + if inst['instance_type'] == 'm1.tiny' or prefix == 'rescue-': + size = None + root_fname += "_sm" + + self._cache_image(fn=self._fetch_image, + target=basepath('disk'), + fname=root_fname, + cow=FLAGS.use_cow_images, image_id=disk_images['image_id'], user=user, project=project, - size=size, - cow=FLAGS.use_cow_images) + size=size) type_data = instance_types.INSTANCE_TYPES[inst['instance_type']] if type_data['local_gb']: - self._create_local(target=basepath('local'), - local_gb=type_data['local_gb'], - cow=FLAGS.use_cow_images) + self._cache_image(fn=self._create_local, + target=basepath('local'), + fname="local_%s" % type_data['local_gb'], + cow=FLAGS.use_cow_images, + local_gb=type_data['local_gb']) # For now, we assume that if we're not using a kernel, we're using a # partitioned disk image where the target partition is the first -- 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/virt/xenapi/vmops.py | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) (limited to 'nova/virt') 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/virt/fake.py | 19 ++++++++++--------- nova/virt/xenapi/vmops.py | 20 ++++++++++---------- nova/virt/xenapi_conn.py | 6 +++--- 3 files changed, 23 insertions(+), 22 deletions(-) (limited to 'nova/virt') 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 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/virt/xenapi/vmops.py | 7 +++++++ nova/virt/xenapi_conn.py | 3 +++ 2 files changed, 10 insertions(+) (limited to 'nova/virt') 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/virt/xenapi/vmops.py | 22 +++++++++------------- nova/virt/xenapi_conn.py | 16 ++-------------- 2 files changed, 11 insertions(+), 27 deletions(-) (limited to 'nova/virt') 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 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/virt/xenapi/vmops.py | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) (limited to 'nova/virt') 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/virt/xenapi/vmops.py | 6 ++++++ nova/virt/xenapi_conn.py | 1 - 2 files changed, 6 insertions(+), 1 deletion(-) (limited to 'nova/virt') 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 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/virt/xenapi/vmops.py | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) (limited to 'nova/virt') 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/virt') 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 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/virt/libvirt_conn.py | 1 - 1 file changed, 1 deletion(-) (limited to 'nova/virt') 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 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/virt/libvirt_conn.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'nova/virt') 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/virt/libvirt_conn.py | 66 ++++++++++++++++++++++++++++++++++++----------- 1 file changed, 51 insertions(+), 15 deletions(-) (limited to 'nova/virt') 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/virt/libvirt_conn.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'nova/virt') 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 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/virt/xenapi/vmops.py | 1 - 1 file changed, 1 deletion(-) (limited to 'nova/virt') 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/virt/xenapi/vmops.py | 1 + 1 file changed, 1 insertion(+) (limited to 'nova/virt') 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/virt') 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 914b0554a092d2b38f292942dc4d7ddea5d99b9a Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya Date: Thu, 13 Jan 2011 15:13:31 -0800 Subject: Modified per sorens review. Moved disk.py Removed disk.partition Changed docstrings Use pid to find nbd devices --- nova/virt/disk.py | 186 ++++++++++++++++++++++++++++++++++++++++++++++ nova/virt/libvirt_conn.py | 10 +-- 2 files changed, 190 insertions(+), 6 deletions(-) create mode 100644 nova/virt/disk.py (limited to 'nova/virt') diff --git a/nova/virt/disk.py b/nova/virt/disk.py new file mode 100644 index 000000000..c5565abfa --- /dev/null +++ b/nova/virt/disk.py @@ -0,0 +1,186 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright 2010 United States Government as represented by the +# Administrator of the National Aeronautics and Space Administration. +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +""" +Utility methods to resize, repartition, and modify disk images. + +Includes injection of SSH PGP keys into authorized_keys file. + +""" + +import os +import tempfile +import time + +from nova import exception +from nova import flags +from nova import log as logging +from nova import utils + + +LOG = logging.getLogger('nova.compute.disk') +FLAGS = flags.FLAGS +flags.DEFINE_integer('minimum_root_size', 1024 * 1024 * 1024 * 10, + 'minimum size in bytes of root partition') +flags.DEFINE_integer('block_size', 1024 * 1024 * 256, + 'block_size to use for dd') + + +def extend(image, size): + """Increase image to size""" + file_size = os.path.getsize(image) + if file_size >= size: + return + utils.execute('truncate -s %s %s' % (size, image)) + # NOTE(vish): attempts to resize filesystem + utils.execute('e2fsck -fp %s' % image, check_exit_code=False) + utils.execute('resize2fs %s' % image, check_exit_code=False) + + +def inject_data(image, key=None, net=None, partition=None, nbd=False): + """Injects a ssh key and optionally net data into a disk image. + + it will mount the image as a fully partitioned disk and attempt to inject + into the specified partition number. + + If partition is not specified it mounts the image as a single partition. + + """ + device = _link_device(image, nbd) + try: + if not partition is None: + # create partition + out, err = utils.execute('sudo kpartx -a %s' % device) + if err: + raise exception.Error(_('Failed to load partition: %s') % err) + mapped_device = '/dev/mapper/%sp%s' % (device.split('/')[-1], + partition) + else: + mapped_device = device + + # We can only loopback mount raw images. If the device isn't there, + # it's normally because it's a .vmdk or a .vdi etc + if not os.path.exists(mapped_device): + raise exception.Error('Mapped device was not found (we can' + ' only inject raw disk images): %s' % + mapped_device) + + # Configure ext2fs so that it doesn't auto-check every N boots + out, err = utils.execute('sudo tune2fs -c 0 -i 0 %s' % mapped_device) + + tmpdir = tempfile.mkdtemp() + try: + # mount loopback to dir + out, err = utils.execute( + 'sudo mount %s %s' % (mapped_device, tmpdir)) + if err: + raise exception.Error(_('Failed to mount filesystem: %s') + % err) + + try: + if key: + # inject key file + _inject_key_into_fs(key, tmpdir) + if net: + _inject_net_into_fs(net, tmpdir) + finally: + # unmount device + utils.execute('sudo umount %s' % mapped_device) + finally: + # remove temporary directory + utils.execute('rmdir %s' % tmpdir) + if not partition is None: + # remove partitions + utils.execute('sudo kpartx -d %s' % device) + finally: + _unlink_device(device, nbd) + + +def _link_device(image, nbd): + """Link image to device using loopback or nbd""" + if nbd: + device = _allocate_device() + utils.execute('sudo qemu-nbd -c %s %s' % (device, image)) + # NOTE(vish): this forks into another process, so give it a chance + # to set up before continuuing + for i in xrange(10): + if os.path.exists("/sys/block/%s/pid" % os.path.basename(device)): + return device + time.sleep(1) + raise exception.Error(_('nbd device %s did not show up') % device) + else: + out, err = utils.execute('sudo losetup --find --show %s' % image) + if err: + raise exception.Error(_('Could not attach image to loopback: %s') + % err) + return out.strip() + + +def _unlink_device(device, nbd): + """Unlink image from device using loopback or nbd""" + if nbd: + utils.execute('sudo qemu-nbd -d %s' % device) + _free_device(device) + else: + utils.execute('sudo losetup --detach %s' % device) + + +_DEVICES = ['/dev/nbd%s' % i for i in xrange(16)] + + +def _allocate_device(): + # NOTE(vish): This assumes no other processes are allocating nbd devices. + # It may race cause a race condition if multiple + # workers are running on a given machine. + while True: + if not _DEVICES: + raise exception.Error(_('No free nbd devices')) + device = _DEVICES.pop() + if not os.path.exists("/sys/block/%s/pid" % os.path.basename(device)): + break + return device + + +def _free_device(device): + _DEVICES.append(device) + + +def _inject_key_into_fs(key, fs): + """Add the given public ssh key to root's authorized_keys. + + key is an ssh key string. + fs is the path to the base of the filesystem into which to inject the key. + """ + sshdir = os.path.join(fs, 'root', '.ssh') + utils.execute('sudo mkdir -p %s' % sshdir) # existing dir doesn't matter + utils.execute('sudo chown root %s' % sshdir) + utils.execute('sudo chmod 700 %s' % sshdir) + keyfile = os.path.join(sshdir, 'authorized_keys') + utils.execute('sudo tee -a %s' % keyfile, '\n' + key.strip() + '\n') + + +def _inject_net_into_fs(net, fs): + """Inject /etc/network/interfaces into the filesystem rooted at fs. + + net is the contents of /etc/network/interfaces. + """ + netdir = os.path.join(os.path.join(fs, 'etc'), 'network') + utils.execute('sudo mkdir -p %s' % netdir) # existing dir doesn't matter + utils.execute('sudo chown root:root %s' % netdir) + utils.execute('sudo chmod 755 %s' % netdir) + netfile = os.path.join(netdir, 'interfaces') + utils.execute('sudo tee %s' % netfile, net) diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py index 760bfef78..c77d25bfd 100644 --- a/nova/virt/libvirt_conn.py +++ b/nova/virt/libvirt_conn.py @@ -58,9 +58,9 @@ from nova import log as logging from nova import utils #from nova.api import context from nova.auth import manager -from nova.compute import disk from nova.compute import instance_types from nova.compute import power_state +from nova.virt import disk from nova.virt import images libvirt = None @@ -495,7 +495,7 @@ class LibvirtConnection(object): return {'token': token, 'host': host, 'port': port} def _cache_image(self, fn, target, fname, cow=False, *args, **kwargs): - """Wrapper to cache a method that creates an image. + """Wrapper for a method that creates an image that caches the image. This wrapper will save the image into a common store and create a copy for use by the hypervisor. @@ -504,7 +504,7 @@ class LibvirtConnection(object): where the image will be saved. fname is used as the filename of the base image. The filename needs - to be fname to a given image. + to be unique to a given image. If cow is True, it will make a CoW image instead of a copy. """ @@ -531,9 +531,7 @@ class LibvirtConnection(object): def _create_local(self, target, local_gb): """Create a blank image of specified size""" - last_mb = local_gb * 1024 - 1 - utils.execute('dd if=/dev/zero of=%s bs=1M count=1 ' - 'seek=%s' % (target, last_mb)) + utils.execute('truncate %s -s %dG' % (target, local_gb)) # TODO(vish): should we format disk by default? def _create_image(self, inst, libvirt_xml, prefix='', disk_images=None): -- cgit From 69c11c27c20c74aced491ecfe78a80872ad6232a Mon Sep 17 00:00:00 2001 From: Andy Smith Date: Fri, 14 Jan 2011 17:54:36 -0800 Subject: pep8 fixes... largely to things from trunk? --- nova/virt/libvirt_conn.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'nova/virt') diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py index 073a8e5bb..b06246135 100644 --- a/nova/virt/libvirt_conn.py +++ b/nova/virt/libvirt_conn.py @@ -995,8 +995,7 @@ class NWFilterFirewall(FirewallDriver): ['no-mac-spoofing', 'no-ip-spoofing', 'no-arp-spoofing', - 'allow-dhcp-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) -- cgit