From 47bc72e5ec27bec349dcfc9468af6325f0a51019 Mon Sep 17 00:00:00 2001 From: Josh Kearney Date: Wed, 12 Jan 2011 12:10:26 -0600 Subject: Start to add rescue/unrescue support --- nova/api/openstack/__init__.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/nova/api/openstack/__init__.py b/nova/api/openstack/__init__.py index f96e2af91..a9b01548a 100644 --- a/nova/api/openstack/__init__.py +++ b/nova/api/openstack/__init__.py @@ -95,6 +95,8 @@ class APIRouter(wsgi.Router): server_members["actions"] = "GET" server_members['suspend'] = 'POST' server_members['resume'] = 'POST' + server_members['rescue'] = 'POST' + server_members['unrescue'] = 'POST' mapper.resource("server", "servers", controller=servers.Controller(), collection={'detail': 'GET'}, -- cgit From 7f2a4fdf5e43620081e163fc46f2ca4fdefd18f3 Mon Sep 17 00:00:00 2001 From: Josh Kearney Date: Wed, 12 Jan 2011 15:07:51 -0600 Subject: Make rescue/unrescue available to API --- nova/api/openstack/servers.py | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/nova/api/openstack/servers.py b/nova/api/openstack/servers.py index 29af82533..3a6c61a3a 100644 --- a/nova/api/openstack/servers.py +++ b/nova/api/openstack/servers.py @@ -283,6 +283,28 @@ class Controller(wsgi.Controller): return faults.Fault(exc.HTTPUnprocessableEntity()) return exc.HTTPAccepted() + def rescue(self, req, id): + """Permit users to rescue the server.""" + context = req.environ["nova.context"] + try: + self.compute_api.rescue(context, id) + except: + readable = traceback.format_exc() + LOG.exception(_("compute.api::rescue %s"), readable) + return faults.Fault(exc.HTTPUnprocessableEntity()) + return exc.HTTPAccepted() + + def unrescue(self, req, id): + """Permit users to unrescue the server.""" + context = req.environ["nova.context"] + try: + self.compute_api.unrescue(context, id) + except: + readable = traceback.format_exc() + LOG.exception(_("compute.api::unrescue %s"), readable) + return faults.Fault(exc.HTTPUnprocessableEntity()) + return exc.HTTPAccepted() + def get_ajax_console(self, req, id): """ Returns a url to an instance's ajaxterm console. """ try: -- cgit From 752bed3311f09e7a43e642231e1638b4252f74a6 Mon Sep 17 00:00:00 2001 From: Josh Kearney Date: Thu, 13 Jan 2011 10:44:29 -0600 Subject: Stubbed out XenServer rescue/unrescue --- nova/virt/xenapi/vmops.py | 8 ++++++++ nova/virt/xenapi_conn.py | 8 ++++++++ 2 files changed, 16 insertions(+) diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py index 7e3585991..8681608e1 100644 --- a/nova/virt/xenapi/vmops.py +++ b/nova/virt/xenapi/vmops.py @@ -264,6 +264,14 @@ class VMOps(object): task = self._session.call_xenapi('Async.VM.resume', vm, False, True) self._wait_with_callback(task, callback) + def rescue(self, instance, callback): + """Rescue the specified instance""" + return True + + def unrescue(self, instance, callback): + """Unrescue the specified instance""" + return True + def get_info(self, instance_id): """Return data about VM instance""" vm = VMHelper.lookup(self._session, instance_id) diff --git a/nova/virt/xenapi_conn.py b/nova/virt/xenapi_conn.py index 45d0738a5..c24ec972b 100644 --- a/nova/virt/xenapi_conn.py +++ b/nova/virt/xenapi_conn.py @@ -169,6 +169,14 @@ class XenAPIConnection(object): """resume the specified instance""" self._vmops.resume(instance, callback) + def rescue(self, instance, callback): + """Rescue the specified instance""" + self._vmops.rescue(instance, callback) + + def unrescue(self, instance, callback): + """Unrescue the specified instance""" + self._vmops.unrescue(instance, callback) + def get_info(self, instance_id): """Return data about VM instance""" return self._vmops.get_info(instance_id) -- cgit From 702d1bd5e58c15e5b7f43e9d56bd591d728ecb71 Mon Sep 17 00:00:00 2001 From: Josh Kearney Date: Thu, 13 Jan 2011 10:47:23 -0600 Subject: Make driver calls compatible --- nova/virt/libvirt_conn.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py index c03046703..797ac1b60 100644 --- a/nova/virt/libvirt_conn.py +++ b/nova/virt/libvirt_conn.py @@ -328,7 +328,7 @@ class LibvirtConnection(object): raise exception.APIError("resume not supported for libvirt") @exception.wrap_exception - def rescue(self, instance): + def rescue(self, instance, callback=None): self.destroy(instance, False) xml = self.to_xml(instance, rescue=True) @@ -358,7 +358,7 @@ class LibvirtConnection(object): return timer.start(interval=0.5, now=True) @exception.wrap_exception - def unrescue(self, instance): + def unrescue(self, instance, callback=None): # NOTE(vish): Because reboot destroys and recreates an instance using # the normal xml file, we can just call reboot here self.reboot(instance) -- cgit From ff6606938749ce5f1a8e430b24d279cde7556c1b Mon Sep 17 00:00:00 2001 From: Josh Kearney Date: Thu, 13 Jan 2011 11:24:11 -0600 Subject: Make libvirt and XenAPI play nice together --- nova/compute/manager.py | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/nova/compute/manager.py b/nova/compute/manager.py index 6b2fc4adb..f1fdd64e6 100644 --- a/nova/compute/manager.py +++ b/nova/compute/manager.py @@ -321,7 +321,11 @@ class ComputeManager(manager.Manager): power_state.NOSTATE, 'rescuing') self.network_manager.setup_compute_network(context, instance_id) - self.driver.rescue(instance_ref) + self.driver.rescue(instance_ref, + lambda result: self._update_state_callback(self, + context, + instance_id, + result)) self._update_state(context, instance_id) @exception.wrap_exception @@ -335,7 +339,11 @@ class ComputeManager(manager.Manager): instance_id, power_state.NOSTATE, 'unrescuing') - self.driver.unrescue(instance_ref) + self.driver.unrescue(instance_ref, + lambda result: self._update_state_callback(self, + context, + instance_id, + result)) self._update_state(context, instance_id) @staticmethod -- cgit From 912e4343cf2622fa42aa4e1c5eac392ce1be96e0 Mon Sep 17 00:00:00 2001 From: Josh Kearney Date: Fri, 14 Jan 2011 11:49:59 -0600 Subject: Create and use a generic handler for RPC calls to compute. --- nova/compute/api.py | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/nova/compute/api.py b/nova/compute/api.py index 90273da36..40b9e33e8 100644 --- a/nova/compute/api.py +++ b/nova/compute/api.py @@ -331,7 +331,7 @@ class API(base.Base): return self.db.instance_get_all(context) def _cast_compute_message(self, method, context, instance_id, host=None): - """Generic handler for RPC calls to compute.""" + """Generic handler for RPC casts to compute.""" if not host: instance = self.get(context, instance_id) host = instance['host'] @@ -339,6 +339,15 @@ class API(base.Base): kwargs = {'method': method, 'args': {'instance_id': instance_id}} rpc.cast(context, queue, kwargs) + def _call_compute_message(self, method, context, instance_id, host=None): + """Generic handler for RPC calls to compute.""" + if not host: + instance = self.get(context, instance_id) + host = instance["host"] + queue = self.db.queue_get_for(context, FLAGS.compute_topic, host) + kwargs = {"method": method, "args": {"instance_id": instance_id}} + return rpc.call(context, queue, kwargs) + def snapshot(self, context, instance_id, name): """Snapshot the given instance.""" self._cast_compute_message('snapshot_instance', context, instance_id) @@ -357,7 +366,10 @@ class API(base.Base): def get_diagnostics(self, context, instance_id): """Retrieve diagnostics for the given instance.""" - self._cast_compute_message('get_diagnostics', context, instance_id) + return self._call_compute_message( + "get_diagnostics", + context, + instance_id) def get_actions(self, context, instance_id): """Retrieve actions for the given instance.""" -- cgit From 3300e692b61dc53ac8ae3bfdbac5bb1019983feb Mon Sep 17 00:00:00 2001 From: Josh Kearney Date: Mon, 17 Jan 2011 12:21:08 -0600 Subject: Add Start/Shutdown support to XenAPI --- nova/virt/xenapi/vmops.py | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py index 96be17d8c..0e6018973 100644 --- a/nova/virt/xenapi/vmops.py +++ b/nova/virt/xenapi/vmops.py @@ -207,6 +207,25 @@ class VMOps(object): logging.debug(_("Finished snapshot and upload for VM %s"), instance) + def start(self, instance): + """Start a VM instance""" + vm = self._get_vm_opaque_ref(instance) + task = self._session.call_xenapi("Async.VM.start", vm, False, False) + self._session.wait_for_task(instance.id, task) + + def shutdown(self, instance, hard=True): + """Shutdown a VM instance""" + vm = self._get_vm_opaque_ref(instance) + if hard: + task = self._session.call_xenapi("Async.VM.hard_shutdown", vm) + else: + # TODO(jk0): clean_shutdown is only supported if XenTools is + # installed and running. What we want to do eventually is try + # this first, and only issue the hard_shutdown if clean_shutdown + # were to fail. + task = self._session.call_xenapi("Async.VM.clean_shutdown", vm) + self._session.wait_for_task(instance.id, task) + def reboot(self, instance): """Reboot VM instance""" vm = self._get_vm_opaque_ref(instance) @@ -320,11 +339,11 @@ class VMOps(object): def rescue(self, instance, callback): """Rescue the specified instance""" - return True + self.shutdown(instance) def unrescue(self, instance, callback): """Unrescue the specified instance""" - return True + self.start(instance) def get_info(self, instance): """Return data about VM instance""" -- cgit From 8c79b0c1995bd9d061c1c379c0034f49cbdb8d05 Mon Sep 17 00:00:00 2001 From: Josh Kearney Date: Mon, 17 Jan 2011 13:31:05 -0600 Subject: Better shutdown handling --- nova/virt/xenapi/vmops.py | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py index 041e2c5bb..f59c1af44 100644 --- a/nova/virt/xenapi/vmops.py +++ b/nova/virt/xenapi/vmops.py @@ -213,18 +213,16 @@ class VMOps(object): task = self._session.call_xenapi("Async.VM.start", vm, False, False) self._session.wait_for_task(instance.id, task) - def shutdown(self, instance, hard=True): + def shutdown(self, instance): """Shutdown a VM instance""" vm = self._get_vm_opaque_ref(instance) - if hard: - task = self._session.call_xenapi("Async.VM.hard_shutdown", vm) - else: - # TODO(jk0): clean_shutdown is only supported if XenTools is - # installed and running. What we want to do eventually is try - # this first, and only issue the hard_shutdown if clean_shutdown - # were to fail. + + try: task = self._session.call_xenapi("Async.VM.clean_shutdown", vm) - self._session.wait_for_task(instance.id, task) + self._session.wait_for_task(instance.id, task) + except self.XenAPI.Failure: + task = self._session.call_xenapi("Async.VM.hard_shutdown", vm) + self._session.wait_for_task(instance.id, task) def reboot(self, instance): """Reboot VM instance""" -- cgit From ecc2afda9fed4e9e69edcc470baf254fac448ce7 Mon Sep 17 00:00:00 2001 From: Josh Kearney Date: Tue, 18 Jan 2011 15:49:42 -0600 Subject: Plug VBD to existing instance and minor cleanup --- nova/tests/xenapi/stubs.py | 2 +- nova/virt/xenapi/fake.py | 2 +- nova/virt/xenapi/vm_utils.py | 12 +++++------- nova/virt/xenapi/vmops.py | 45 +++++++++++++++++++++++++++++++++---------- nova/virt/xenapi/volumeops.py | 2 +- nova/virt/xenapi_conn.py | 15 +++++++++------ 6 files changed, 52 insertions(+), 26 deletions(-) diff --git a/nova/tests/xenapi/stubs.py b/nova/tests/xenapi/stubs.py index 292bd9ba9..313668826 100644 --- a/nova/tests/xenapi/stubs.py +++ b/nova/tests/xenapi/stubs.py @@ -37,7 +37,7 @@ def stubout_instance_snapshot(stubs): return self.rv done = FakeEvent() - self._poll_task(id, task, done) + self._poll_task(task, id, done) rv = done.wait() return rv diff --git a/nova/virt/xenapi/fake.py b/nova/virt/xenapi/fake.py index 96d8f5fc8..92ccfd975 100644 --- a/nova/virt/xenapi/fake.py +++ b/nova/virt/xenapi/fake.py @@ -335,7 +335,7 @@ class SessionBase(object): field in _db_content[cls][ref]): return _db_content[cls][ref][field] - LOG.debuug(_('Raising NotImplemented')) + LOG.debug(_('Raising NotImplemented')) raise NotImplementedError( _('xenapi.fake does not have an implementation for %s or it has ' 'been called with the wrong number of arguments') % name) diff --git a/nova/virt/xenapi/vm_utils.py b/nova/virt/xenapi/vm_utils.py index eb0393d2a..d5c6a85b4 100644 --- a/nova/virt/xenapi/vm_utils.py +++ b/nova/virt/xenapi/vm_utils.py @@ -179,9 +179,7 @@ class VMHelper(HelperBase): """Destroy VBD from host database""" try: task = session.call_xenapi('Async.VBD.destroy', vbd_ref) - #FIXME(armando): find a solution to missing instance_id - #with Josh Kearney - session.wait_for_task(0, task) + session.wait_for_task(task) except cls.XenAPI.Failure, exc: LOG.exception(exc) raise StorageError(_('Unable to destroy VBD %s') % vbd_ref) @@ -222,7 +220,7 @@ class VMHelper(HelperBase): original_parent_uuid = get_vhd_parent_uuid(session, vm_vdi_ref) task = session.call_xenapi('Async.VM.snapshot', vm_ref, label) - template_vm_ref = session.wait_for_task(instance_id, task) + template_vm_ref = session.wait_for_task(task, instance_id) template_vdi_rec = get_vdi_for_vm_safely(session, template_vm_ref)[1] template_vdi_uuid = template_vdi_rec["uuid"] @@ -250,7 +248,7 @@ class VMHelper(HelperBase): kwargs = {'params': pickle.dumps(params)} task = session.async_call_plugin('glance', 'put_vdis', kwargs) - session.wait_for_task(instance_id, task) + session.wait_for_task(task, instance_id) @classmethod def fetch_image(cls, session, instance_id, image, user, project, type): @@ -272,7 +270,7 @@ class VMHelper(HelperBase): if type == ImageType.DISK_RAW: args['raw'] = 'true' task = session.async_call_plugin('objectstore', fn, args) - uuid = session.wait_for_task(instance_id, task) + uuid = session.wait_for_task(task, instance_id) return uuid @classmethod @@ -409,7 +407,7 @@ def get_vhd_parent_uuid(session, vdi_ref): def scan_sr(session, instance_id, sr_ref): LOG.debug(_("Re-scanning SR %s"), sr_ref) task = session.call_xenapi('Async.SR.scan', sr_ref) - session.wait_for_task(instance_id, task) + session.wait_for_task(task, instance_id) def wait_for_vhd_coalesce(session, instance_id, sr_ref, vdi_ref, diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py index f59c1af44..0de7c8d42 100644 --- a/nova/virt/xenapi/vmops.py +++ b/nova/virt/xenapi/vmops.py @@ -211,24 +211,23 @@ class VMOps(object): """Start a VM instance""" vm = self._get_vm_opaque_ref(instance) task = self._session.call_xenapi("Async.VM.start", vm, False, False) - self._session.wait_for_task(instance.id, task) + self._session.wait_for_task(task, instance.id) def shutdown(self, instance): """Shutdown a VM instance""" vm = self._get_vm_opaque_ref(instance) - try: task = self._session.call_xenapi("Async.VM.clean_shutdown", vm) - self._session.wait_for_task(instance.id, task) + self._session.wait_for_task(task, instance.id) except self.XenAPI.Failure: task = self._session.call_xenapi("Async.VM.hard_shutdown", vm) - self._session.wait_for_task(instance.id, task) + self._session.wait_for_task(task, instance.id) 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) + self._session.wait_for_task(task, instance.id) def set_admin_password(self, instance, new_pass): """Set the root/admin password on the VM instance. This is done via @@ -284,7 +283,7 @@ class VMOps(object): if shutdown: try: task = self._session.call_xenapi('Async.VM.hard_shutdown', vm) - self._session.wait_for_task(instance.id, task) + self._session.wait_for_task(task, instance.id) except self.XenAPI.Failure, exc: LOG.exception(exc) @@ -293,20 +292,20 @@ class VMOps(object): for vdi in vdis: try: task = self._session.call_xenapi('Async.VDI.destroy', vdi) - self._session.wait_for_task(instance.id, task) + self._session.wait_for_task(task, instance.id) except self.XenAPI.Failure, exc: LOG.exception(exc) # VM Destroy try: task = self._session.call_xenapi('Async.VM.destroy', vm) - self._session.wait_for_task(instance.id, task) + self._session.wait_for_task(task, instance.id) except self.XenAPI.Failure, exc: LOG.exception(exc) def _wait_with_callback(self, instance_id, task, callback): ret = None try: - ret = self._session.wait_for_task(instance_id, task) + ret = self._session.wait_for_task(task, instance_id) except self.XenAPI.Failure, exc: LOG.exception(exc) callback(ret) @@ -337,10 +336,36 @@ class VMOps(object): def rescue(self, instance, callback): """Rescue the specified instance""" + vm = self._get_vm_opaque_ref(instance) + target_vm = VMHelper.lookup(self._session, "instance-00000001") + self.shutdown(instance) + vbd = self._session.get_xenapi().VM.get_VBDs(vm)[0] + vdi_ref = self._session.get_xenapi().VBD.get_record(vbd)["VDI"] + vbd_ref = VMHelper.create_vbd( + self._session, + target_vm, + vdi_ref, + 1, + False) + + # Plug the VBD into the target instance + self._session.call_xenapi("Async.VBD.plug", vbd_ref) + def unrescue(self, instance, callback): """Unrescue the specified instance""" + vm = self._get_vm_opaque_ref(instance) + target_vm = VMHelper.lookup(self._session, "instance-00000001") + + vbds = self._session.get_xenapi().VM.get_VBDs(target_vm) + + for vbd_ref in vbds: + vbd = self._session.get_xenapi().VBD.get_record(vbd_ref) + if vbd["userdevice"] == str(1): + VMHelper.unplug_vbd(self._session, vbd_ref) + VMHelper.destroy_vbd(self._session, vbd_ref) + self.start(instance) def get_info(self, instance): @@ -425,7 +450,7 @@ class VMOps(object): args.update(addl_args) try: task = self._session.async_call_plugin(plugin, method, args) - ret = self._session.wait_for_task(instance_id, task) + ret = self._session.wait_for_task(task, instance_id) except self.XenAPI.Failure, e: ret = None err_trace = e.details[-1] diff --git a/nova/virt/xenapi/volumeops.py b/nova/virt/xenapi/volumeops.py index 189f968c6..0c5f36f6b 100644 --- a/nova/virt/xenapi/volumeops.py +++ b/nova/virt/xenapi/volumeops.py @@ -85,7 +85,7 @@ class VolumeOps(object): try: task = self._session.call_xenapi('Async.VBD.plug', vbd_ref) - self._session.wait_for_task(vol_rec['deviceNumber'], task) + self._session.wait_for_task(task, vol_rec['deviceNumber']) except self.XenAPI.Failure, exc: LOG.exception(exc) VolumeHelper.destroy_iscsi_storage(self._session, diff --git a/nova/virt/xenapi_conn.py b/nova/virt/xenapi_conn.py index e9793e6a7..07bc0fd84 100644 --- a/nova/virt/xenapi_conn.py +++ b/nova/virt/xenapi_conn.py @@ -290,7 +290,7 @@ class XenAPISession(object): self._session.xenapi.Async.host.call_plugin, self.get_xenapi_host(), plugin, fn, args) - def wait_for_task(self, id, task): + def wait_for_task(self, task, id=None): """Return the result of the given task. The task is polled until it completes. Not re-entrant.""" done = event.Event() @@ -317,10 +317,11 @@ class XenAPISession(object): try: name = self._session.xenapi.task.get_name_label(task) status = self._session.xenapi.task.get_status(task) - action = dict( - instance_id=int(id), - action=name[0:255], # Ensure action is never > 255 - error=None) + if id: + action = dict( + instance_id=int(id), + action=name[0:255], # Ensure action is never > 255 + error=None) if status == "pending": return elif status == "success": @@ -339,7 +340,9 @@ class XenAPISession(object): status, error_info)) done.send_exception(self.XenAPI.Failure(error_info)) - db.instance_action_create(context.get_admin_context(), action) + + if id: + db.instance_action_create(context.get_admin_context(), action) except self.XenAPI.Failure, exc: LOG.warn(exc) done.send_exception(*sys.exc_info()) -- cgit From 2c0f1d78927c14f1d155e617a066b09a00acb100 Mon Sep 17 00:00:00 2001 From: Cerberus Date: Tue, 18 Jan 2011 17:40:36 -0600 Subject: Basic stubbing throughout the stack --- nova/api/openstack/servers.py | 37 ++++++++++++++++++++++++++++++++++++- nova/compute/api.py | 14 ++++++++++++++ nova/compute/manager.py | 13 +++++++++++++ nova/virt/fake.py | 12 ++++++++++++ nova/virt/xenapi/vmops.py | 4 ++++ nova/virt/xenapi_conn.py | 4 ++++ 6 files changed, 83 insertions(+), 1 deletion(-) diff --git a/nova/api/openstack/servers.py b/nova/api/openstack/servers.py index 8cbcebed2..06104b37e 100644 --- a/nova/api/openstack/servers.py +++ b/nova/api/openstack/servers.py @@ -182,9 +182,44 @@ class Controller(wsgi.Controller): return exc.HTTPNoContent() def action(self, req, id): - """ Multi-purpose method used to reboot, rebuild, and + """ Multi-purpose method used to reboot, rebuild, or resize a server """ + + actions = { + 'reboot':self._action_reboot, + 'resize':self._action_resize, + 'confirmResize':self._action_confirm_resize, + 'revertResize':self._action_revert_resize, + 'rebuild':self._action_rebuild + } + input_dict = self._deserialize(req.body, req) + for key in actions.keys(): + if key in input_dict: + return actions[key](input_dict, req, id) + return faults.Fault(exc.HTTPNotImplemented()) + + def _action_confirm_resize(self, input_dict, req, id): + return fault.Fault(exc.HTTPNotImplemented()) + + def _action_revert_resize(self, input_dict, req, id): + return fault.Fault(exc.HTTPNotImplemented()) + + def _action_rebuild(self, input_dict, req, id): + return fault.Fault(exc.HTTPNotImplemented()) + + def _action_resize(self, input_dict, req, id): + """ Resizes a given instance to the flavor size requested """ + try: + resize_flavor = input_dict['resize']['flavorId'] + self.compute_api.resize(req.environ['nova.context'], id, + flavor_id) + except: + return faults.Fault(exc.HTTPUnprocessableEntity()) + return fault.Fault(exc.HTTPAccepted()) + + + def _action_reboot(self, input_dict, req, id): #TODO(sandy): rebuild/resize not supported. try: reboot_type = input_dict['reboot']['type'] diff --git a/nova/compute/api.py b/nova/compute/api.py index cc85ec691..22d5964c6 100644 --- a/nova/compute/api.py +++ b/nova/compute/api.py @@ -361,6 +361,20 @@ class API(base.Base): """Reboot the given instance.""" self._cast_compute_message('reboot_instance', context, instance_id) + def revert_resize(self, context, instance_id): + """Reverts a resize, deleting the 'new' instance in the process""" + raise NotImplemented() + + def confirm_resize(self, context, instance_id): + """Confirms a migration/resize, deleting the 'old' instance in the + process.""" + raise NotImplemented() + + def resize(self, context, instance_id, flavor_id): + """Resize a running instance.""" + self._cast_compute_message('resize_instance', context, instance_id, + flavor_id) + def pause(self, context, instance_id): """Pause the given instance.""" self._cast_compute_message('pause_instance', context, instance_id) diff --git a/nova/compute/manager.py b/nova/compute/manager.py index 613ee45f6..714cec209 100644 --- a/nova/compute/manager.py +++ b/nova/compute/manager.py @@ -376,6 +376,19 @@ class ComputeManager(manager.Manager): """Update instance state when async task completes.""" self._update_state(context, instance_id) + @exception.wrap_exception + @checks_instance_lock + def resize_instance(self, context, instance_id, flavor_id): + """Moves a running instance to another host, possibly changing the RAM + and disk size in the process""" + context = context.elevated() + instance_ref = self.db.instance_get(context. instance_id) + LOG.audit(_('instance %s: migrating'), instance_id, context=context) + self.db.instance_set_state(context, instance_id, power_state.RUNNING, + 'migrating') + self.driver.resize(instance_ref, flavor_id) + self._update_state(context, instance_id) + @exception.wrap_exception @checks_instance_lock def pause_instance(self, context, instance_id): diff --git a/nova/virt/fake.py b/nova/virt/fake.py index a57a8f43b..a6fe24c3e 100644 --- a/nova/virt/fake.py +++ b/nova/virt/fake.py @@ -138,6 +138,18 @@ class FakeConnection(object): """ pass + def resize(self, instance, flavor): + """ + Resizes/Migrates the specified instance. + + The flavor parameter determines whether or not the instance RAM and + disk space are modified, and if so, to what size. + + The work will be done asynchronously. This function returns a task + that allows the caller to detect when it is complete. + """ + pass + def set_admin_password(self, instance, new_pass): """ Set the root password on the specified instance. diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py index 6e359ef82..50ee24325 100644 --- a/nova/virt/xenapi/vmops.py +++ b/nova/virt/xenapi/vmops.py @@ -207,6 +207,10 @@ class VMOps(object): logging.debug(_("Finished snapshot and upload for VM %s"), instance) + def resize(self, instance, flavor): + """Resize a running instance by changing it's RAM and disk size """ + raise NotImplementedError() + def reboot(self, instance): """Reboot VM instance""" vm = self._get_vm_opaque_ref(instance) diff --git a/nova/virt/xenapi_conn.py b/nova/virt/xenapi_conn.py index 689844f34..c850dbf80 100644 --- a/nova/virt/xenapi_conn.py +++ b/nova/virt/xenapi_conn.py @@ -145,6 +145,10 @@ class XenAPIConnection(object): """ Create snapshot from a running VM instance """ self._vmops.snapshot(instance, name) + def resize(self, instance, flavor): + """Resize a VM instance""" + raise NotImplementedError() + def reboot(self, instance): """Reboot VM instance""" self._vmops.reboot(instance) -- cgit From 2b2f08dc1dfe1b55433c9122d7d42a480cdb5e67 Mon Sep 17 00:00:00 2001 From: Josh Kearney Date: Tue, 18 Jan 2011 17:57:11 -0600 Subject: Fixed unit tests --- nova/tests/xenapi/stubs.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/nova/tests/xenapi/stubs.py b/nova/tests/xenapi/stubs.py index c1ba25e10..13603717c 100644 --- a/nova/tests/xenapi/stubs.py +++ b/nova/tests/xenapi/stubs.py @@ -27,7 +27,7 @@ def stubout_instance_snapshot(stubs): def fake_fetch_image(cls, session, instance_id, image, user, project, type): # Stubout wait_for_task - def fake_wait_for_task(self, id, task): + def fake_wait_for_task(self, task, id=None): class FakeEvent: def send(self, value): @@ -37,7 +37,7 @@ def stubout_instance_snapshot(stubs): return self.rv done = FakeEvent() - self._poll_task(task, id, done) + self._poll_task(id, task, done) rv = done.wait() return rv -- cgit From bb727b7032104d3d3966108d846dd3e5b8a1a37d Mon Sep 17 00:00:00 2001 From: Josh Kearney Date: Tue, 18 Jan 2011 17:59:26 -0600 Subject: Fix merge conflict --- nova/virt/xenapi/vm_utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nova/virt/xenapi/vm_utils.py b/nova/virt/xenapi/vm_utils.py index 9600584b4..5f5ac254d 100644 --- a/nova/virt/xenapi/vm_utils.py +++ b/nova/virt/xenapi/vm_utils.py @@ -329,7 +329,7 @@ class VMHelper(HelperBase): #let the plugin copy the correct number of bytes args['image-size'] = str(vdi_size) task = session.async_call_plugin('glance', fn, args) - filename = session.wait_for_task(instance_id, task) + filename = session.wait_for_task(task, instance_id) #remove the VDI as it is not needed anymore session.get_xenapi().VDI.destroy(vdi) LOG.debug(_("Kernel/Ramdisk VDI %s destroyed"), vdi) -- cgit From 4b77a532fd641947c9259327cef9104f689f1127 Mon Sep 17 00:00:00 2001 From: Josh Kearney Date: Tue, 18 Jan 2011 18:09:58 -0600 Subject: Fixed unit tests --- nova/tests/xenapi/stubs.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nova/tests/xenapi/stubs.py b/nova/tests/xenapi/stubs.py index 13603717c..66d232a7f 100644 --- a/nova/tests/xenapi/stubs.py +++ b/nova/tests/xenapi/stubs.py @@ -27,7 +27,7 @@ def stubout_instance_snapshot(stubs): def fake_fetch_image(cls, session, instance_id, image, user, project, type): # Stubout wait_for_task - def fake_wait_for_task(self, task, id=None): + def fake_wait_for_task(self, task, id): class FakeEvent: def send(self, value): -- cgit From dd70c9f3909c800d72d73d507e9da05a6ed932de Mon Sep 17 00:00:00 2001 From: Josh Kearney Date: Wed, 19 Jan 2011 11:20:16 -0600 Subject: Fixed unit tests --- nova/virt/xenapi/vm_utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nova/virt/xenapi/vm_utils.py b/nova/virt/xenapi/vm_utils.py index 5f5ac254d..7484472d7 100644 --- a/nova/virt/xenapi/vm_utils.py +++ b/nova/virt/xenapi/vm_utils.py @@ -371,7 +371,7 @@ class VMHelper(HelperBase): args = {} args['vdi-ref'] = vdi_ref task = session.async_call_plugin('objectstore', fn, args) - pv_str = session.wait_for_task(instance_id, task) + pv_str = session.wait_for_task(task, instance_id) pv = None if pv_str.lower() == 'true': pv = True -- cgit From 671f27322156615643ce9194a26bec66819c0c78 Mon Sep 17 00:00:00 2001 From: Josh Kearney Date: Wed, 26 Jan 2011 10:34:57 -0600 Subject: Cleaned up _start() and _shutdown() --- nova/virt/xenapi/vmops.py | 39 ++++++++++++++++----------------------- 1 file changed, 16 insertions(+), 23 deletions(-) diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py index e348aee95..e4aff2313 100644 --- a/nova/virt/xenapi/vmops.py +++ b/nova/virt/xenapi/vmops.py @@ -208,22 +208,6 @@ class VMOps(object): logging.debug(_("Finished snapshot and upload for VM %s"), instance) - def start(self, instance): - """Start a VM instance""" - vm = self._get_vm_opaque_ref(instance) - task = self._session.call_xenapi("Async.VM.start", vm, False, False) - self._session.wait_for_task(task, instance.id) - - def shutdown(self, instance): - """Shutdown a VM instance""" - vm = self._get_vm_opaque_ref(instance) - try: - task = self._session.call_xenapi("Async.VM.clean_shutdown", vm) - self._session.wait_for_task(task, instance.id) - except self.XenAPI.Failure: - task = self._session.call_xenapi("Async.VM.hard_shutdown", vm) - self._session.wait_for_task(task, instance.id) - def reboot(self, instance): """Reboot VM instance""" vm = self._get_vm_opaque_ref(instance) @@ -268,8 +252,13 @@ class VMOps(object): raise RuntimeError(resp_dict['message']) return resp_dict['message'] + def _start(self, instance, vm): + """Start an instance""" + task = self._session.call_xenapi("Async.VM.start", vm, False, False) + self._session.wait_for_task(task, instance.id) + def _shutdown(self, instance, vm): - """Shutdown an instance """ + """Shutdown an instance""" state = self.get_info(instance['name'])['state'] if state == power_state.SHUTDOWN: LOG.warn(_("VM %(vm)s already halted, skipping shutdown...") % @@ -277,8 +266,12 @@ class VMOps(object): return try: - task = self._session.call_xenapi('Async.VM.hard_shutdown', vm) - self._session.wait_for_task(task, instance.id) + try: + task = self._session.call_xenapi("Async.VM.clean_shutdown", vm) + self._session.wait_for_task(task, instance.id) + except self.XenAPI.Failure: + task = self._session.call_xenapi("Async.VM.hard_shutdown", vm) + self._session.wait_for_task(task, instance.id) except self.XenAPI.Failure, exc: LOG.exception(exc) @@ -368,9 +361,9 @@ class VMOps(object): def rescue(self, instance, callback): """Rescue the specified instance""" vm = self._get_vm_opaque_ref(instance) - target_vm = VMHelper.lookup(self._session, "instance-00000001") + target_vm = VMHelper.lookup(self._session, "instance-00000012") - self.shutdown(instance) + self._shutdown(instance, vm) vbd = self._session.get_xenapi().VM.get_VBDs(vm)[0] vdi_ref = self._session.get_xenapi().VBD.get_record(vbd)["VDI"] @@ -387,7 +380,7 @@ class VMOps(object): def unrescue(self, instance, callback): """Unrescue the specified instance""" vm = self._get_vm_opaque_ref(instance) - target_vm = VMHelper.lookup(self._session, "instance-00000001") + target_vm = VMHelper.lookup(self._session, "instance-00000012") vbds = self._session.get_xenapi().VM.get_VBDs(target_vm) @@ -397,7 +390,7 @@ class VMOps(object): VMHelper.unplug_vbd(self._session, vbd_ref) VMHelper.destroy_vbd(self._session, vbd_ref) - self.start(instance) + self._start(instance, vm) def get_info(self, instance): """Return data about VM instance""" -- cgit From a776844e38c7e747397785a6ce6b1de1b043d850 Mon Sep 17 00:00:00 2001 From: Ken Pepple Date: Tue, 1 Feb 2011 18:34:46 -0800 Subject: initial support for dynamic instance_types: db migration and model, stub tests and stub methods. --- bin/nova-manage | 41 +++++++++++- nova/compute/instance_types.py | 12 ++++ .../sqlalchemy/migrate_repo/versions/003_cactus.py | 76 ++++++++++++++++++++++ nova/db/sqlalchemy/models.py | 10 +++ nova/tests/api/openstack/test_flavors.py | 10 +++ 5 files changed, 148 insertions(+), 1 deletion(-) create mode 100644 nova/db/sqlalchemy/migrate_repo/versions/003_cactus.py diff --git a/bin/nova-manage b/bin/nova-manage index 1b70ebf17..952bf4fd1 100755 --- a/bin/nova-manage +++ b/bin/nova-manage @@ -609,6 +609,44 @@ class VolumeCommands(object): "mountpoint": volume['mountpoint']}}) +class InstanceTypesCommands(object): + """Class for managing instance types / flavors.""" + + def create(self, name, memory, vcpus, localstorage): + """Creates instance types / flavors + arguments: name memory vcpus localstorage""" + #for address in IPy.IP(range): + # db.floating_ip_create(context.get_admin_context(), + # {'address': str(address), + # 'host': host}) + + def delete(self, name): + """Deletes instance types / flavors + arguments: name""" + #for address in IPy.IP(ip_range): + # db.floating_ip_destroy(context.get_admin_context(), + # str(address)) + + def list(self): + """Lists all instance types / flavors + arguments: """ + #ctxt = context.get_admin_context() + #if host == None: + # floating_ips = db.floating_ip_get_all(ctxt) + #else: + # floating_ips = db.floating_ip_get_all_by_host(ctxt, host) + #for floating_ip in floating_ips: + # instance = None + # if floating_ip['fixed_ip']: + # instance = floating_ip['fixed_ip']['instance']['ec2_id'] + # print "%s\t%s\t%s" % (floating_ip['host'], + # floating_ip['address'], + # instance) + # print "%-10s %-10s %-8s %s %s" % (svc['host'], svc['binary'], + # active, art, + # svc['updated_at']) + + CATEGORIES = [ ('user', UserCommands), ('project', ProjectCommands), @@ -620,7 +658,8 @@ CATEGORIES = [ ('service', ServiceCommands), ('log', LogCommands), ('db', DbCommands), - ('volume', VolumeCommands)] + ('volume', VolumeCommands), + ('instance_types', InstanceTypesCommands)] def lazy_match(name, key_value_tuples): diff --git a/nova/compute/instance_types.py b/nova/compute/instance_types.py index 196d6a8df..1b3d0d0eb 100644 --- a/nova/compute/instance_types.py +++ b/nova/compute/instance_types.py @@ -48,3 +48,15 @@ def get_by_flavor_id(flavor_id): if details['flavorid'] == flavor_id: return instance_type return FLAGS.default_instance_type + + +def list_flavors(): + return instance_type + + +def create_flavor(): + return instance_type + + +def delete_flavor(): + return instance_type diff --git a/nova/db/sqlalchemy/migrate_repo/versions/003_cactus.py b/nova/db/sqlalchemy/migrate_repo/versions/003_cactus.py new file mode 100644 index 000000000..cc4a7aec0 --- /dev/null +++ b/nova/db/sqlalchemy/migrate_repo/versions/003_cactus.py @@ -0,0 +1,76 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# 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. + +from sqlalchemy import * +from migrate import * + + +from nova import log as logging + + +meta = MetaData() + + +# +# New Tables +# +# Here are the old static instance types +# INSTANCE_TYPES = { +# 'm1.tiny': dict(memory_mb=512, vcpus=1, local_gb=0, flavorid=1), +# 'm1.small': dict(memory_mb=2048, vcpus=1, local_gb=20, flavorid=2), +# 'm1.medium': dict(memory_mb=4096, vcpus=2, local_gb=40, flavorid=3), +# 'm1.large': dict(memory_mb=8192, vcpus=4, local_gb=80, flavorid=4), +# 'm1.xlarge': dict(memory_mb=16384, vcpus=8, local_gb=160, flavorid=5)} +instance_types = Table('instance_types', meta, + Column('created_at', DateTime(timezone=False)), + Column('deleted_at', DateTime(timezone=False)), + Column('deleted', Boolean(create_constraint=True, name=None)), + Column('id', Integer(), primary_key=True, nullable=False), + Column('memory_mb', Integer(), nullable=False), + Column('vcpus', Integer(), nullable=False), + Column('local_gb', Integer(), nullable=False), + Column('flavorid', Integer(), nullable=False), + ) + + +def upgrade(migrate_engine): + # Upgrade operations go here + # Don't create your own engine; bind migrate_engine + # to your metadata + meta.bind = migrate_engine + try: + instance_types.create() + except Exception: + logging.info(repr(table)) + logging.exception('Exception while creating table') + raise + + # TODO(ken-pepple) fix this to pre-populate the default EC2 types + #INSTANCE_TYPES = { + # 'm1.tiny': dict(memory_mb=512, vcpus=1, local_gb=0, flavorid=1), + # 'm1.small': dict(memory_mb=2048, vcpus=1, local_gb=20, flavorid=2), + # 'm1.medium': dict(memory_mb=4096, vcpus=2, local_gb=40, flavorid=3), + # 'm1.large': dict(memory_mb=8192, vcpus=4, local_gb=80, flavorid=4), + # 'm1.xlarge': dict(memory_mb=16384, vcpus=8, local_gb=160, flavorid=5)} + # for instance_type in INSTANCE_TYPES: + # try: + # prepopulate tables with EC2 types + + +def downgrade(migrate_engine): + # Operations to reverse the above upgrade go here. + # # Operations to reverse the above upgrade go here. + # for table in (instance_types): + # table.drop() + pass diff --git a/nova/db/sqlalchemy/models.py b/nova/db/sqlalchemy/models.py index c54ebe3ba..762425670 100644 --- a/nova/db/sqlalchemy/models.py +++ b/nova/db/sqlalchemy/models.py @@ -210,6 +210,16 @@ class InstanceActions(BASE, NovaBase): error = Column(Text) +class InstanceTypes(BASE, NovaBase): + """Represent possible instance_types or flavor of VM offered""" + __tablename__ = "instance_types" + id = Column(Integer, primary_key=True) + memory_mb = Column(Integer) + vcpus = Column(Integer) + local_gb = Column(Integer) + flavorid = Column(Integer) + + class Volume(BASE, NovaBase): """Represents a block storage device that can be attached to a vm.""" __tablename__ = 'volumes' diff --git a/nova/tests/api/openstack/test_flavors.py b/nova/tests/api/openstack/test_flavors.py index 1bdaea161..05624c5a9 100644 --- a/nova/tests/api/openstack/test_flavors.py +++ b/nova/tests/api/openstack/test_flavors.py @@ -44,5 +44,15 @@ class FlavorsTest(unittest.TestCase): def test_get_flavor_by_id(self): pass + def test_create_favor(self): + pass + + def test_delete_flavor(self): + pass + + def test_list_flavors(self): + pass + + if __name__ == '__main__': unittest.main() -- cgit From 6d2e2c52012abac8cab322357ce0ffd0ffc2fbaf Mon Sep 17 00:00:00 2001 From: Cerberus Date: Wed, 2 Feb 2011 10:49:02 -0600 Subject: Casting to the scheduler --- nova/compute/api.py | 10 +++++++--- nova/rpc.py | 2 +- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/nova/compute/api.py b/nova/compute/api.py index 0faa066b5..283845709 100644 --- a/nova/compute/api.py +++ b/nova/compute/api.py @@ -404,10 +404,14 @@ class API(base.Base): process.""" raise NotImplemented() - def resize(self, context, instance_id, flavor_id): + def resize(self, context, instance_id, flavor): """Resize a running instance.""" - self._cast_compute_message('resize_instance', context, instance_id, - flavor_id) + rpc.cast(context, + FLAGS.scheduler_topic, + {"method": "resize_instance", + "args": {"topic": FLAGS.compute_topic, + "instance_id": instance_id, + "flavor": flavor}}) def pause(self, context, instance_id): """Pause the given instance.""" diff --git a/nova/rpc.py b/nova/rpc.py index 01fc6d44b..c4c938f4d 100644 --- a/nova/rpc.py +++ b/nova/rpc.py @@ -119,7 +119,7 @@ class Consumer(messaging.Consumer): LOG.error(_("Reconnected to queue")) self.failed_connection = False # NOTE(vish): This is catching all errors because we really don't - # exceptions to be logged 10 times a second if some + # want exceptions to be logged 10 times a second if some # persistent failure occurs. except Exception: # pylint: disable-msg=W0703 if not self.failed_connection: -- cgit From 563a77fd4aa80da9bddac5cf7f8f27ed2dedb39d Mon Sep 17 00:00:00 2001 From: Ken Pepple Date: Thu, 3 Feb 2011 17:52:19 -0800 Subject: added seed data to migration --- .../sqlalchemy/migrate_repo/versions/003_cactus.py | 53 ++++++++++++---------- 1 file changed, 30 insertions(+), 23 deletions(-) diff --git a/nova/db/sqlalchemy/migrate_repo/versions/003_cactus.py b/nova/db/sqlalchemy/migrate_repo/versions/003_cactus.py index cc4a7aec0..c7e61dc05 100644 --- a/nova/db/sqlalchemy/migrate_repo/versions/003_cactus.py +++ b/nova/db/sqlalchemy/migrate_repo/versions/003_cactus.py @@ -15,9 +15,11 @@ from sqlalchemy import * from migrate import * - +from nova import api +from nova import db from nova import log as logging +import time meta = MetaData() @@ -25,17 +27,14 @@ meta = MetaData() # # New Tables # -# Here are the old static instance types -# INSTANCE_TYPES = { -# 'm1.tiny': dict(memory_mb=512, vcpus=1, local_gb=0, flavorid=1), -# 'm1.small': dict(memory_mb=2048, vcpus=1, local_gb=20, flavorid=2), -# 'm1.medium': dict(memory_mb=4096, vcpus=2, local_gb=40, flavorid=3), -# 'm1.large': dict(memory_mb=8192, vcpus=4, local_gb=80, flavorid=4), -# 'm1.xlarge': dict(memory_mb=16384, vcpus=8, local_gb=160, flavorid=5)} instance_types = Table('instance_types', meta, Column('created_at', DateTime(timezone=False)), + Column('updated_at', DateTime(timezone=False)), Column('deleted_at', DateTime(timezone=False)), Column('deleted', Boolean(create_constraint=True, name=None)), + Column('name', + String(length=255, convert_unicode=False, assert_unicode=None, + unicode_error=None, _warn_on_bytestring=False)), Column('id', Integer(), primary_key=True, nullable=False), Column('memory_mb', Integer(), nullable=False), Column('vcpus', Integer(), nullable=False), @@ -53,24 +52,32 @@ def upgrade(migrate_engine): instance_types.create() except Exception: logging.info(repr(table)) - logging.exception('Exception while creating table') + logging.exception('Exception while creating instance_types table') raise - # TODO(ken-pepple) fix this to pre-populate the default EC2 types - #INSTANCE_TYPES = { - # 'm1.tiny': dict(memory_mb=512, vcpus=1, local_gb=0, flavorid=1), - # 'm1.small': dict(memory_mb=2048, vcpus=1, local_gb=20, flavorid=2), - # 'm1.medium': dict(memory_mb=4096, vcpus=2, local_gb=40, flavorid=3), - # 'm1.large': dict(memory_mb=8192, vcpus=4, local_gb=80, flavorid=4), - # 'm1.xlarge': dict(memory_mb=16384, vcpus=8, local_gb=160, flavorid=5)} - # for instance_type in INSTANCE_TYPES: - # try: - # prepopulate tables with EC2 types + # Here are the old static instance types + INSTANCE_TYPES = { + 'm1.tiny': dict(memory_mb=512, vcpus=1, local_gb=0, flavorid=1), + 'm1.small': dict(memory_mb=2048, vcpus=1, local_gb=20, flavorid=2), + 'm1.medium': dict(memory_mb=4096, vcpus=2, local_gb=40, flavorid=3), + 'm1.large': dict(memory_mb=8192, vcpus=4, local_gb=80, flavorid=4), + 'm1.xlarge': dict(memory_mb=16384, vcpus=8, local_gb=160, flavorid=5)} + try: + i = instance_types.insert() + for name, values in INSTANCE_TYPES.iteritems(): + # FIXME(kpepple) should we be seeding created_at / updated_at ? + # the_time = time.strftime("%Y-%m-%d %H:%M:%S") + i.execute({'name': name, 'memory_mb': values["memory_mb"], + 'vcpus': values["vcpus"], + 'local_gb': values["local_gb"], + 'flavorid': values["flavorid"]}) + except Exception: + logging.info(repr(table)) + logging.exception('Exception while seeding instance_types table') + raise def downgrade(migrate_engine): # Operations to reverse the above upgrade go here. - # # Operations to reverse the above upgrade go here. - # for table in (instance_types): - # table.drop() - pass + for table in (instance_types): + table.drop() -- cgit From 9be0770208b0e75c7d93ba10165b82d5be11be27 Mon Sep 17 00:00:00 2001 From: Ken Pepple Date: Thu, 3 Feb 2011 17:57:46 -0800 Subject: flagged all INSTANCE_TYPES usage with FIXME comment. Added basic usage to nova-manage (needs formatting). created api methods. --- bin/nova-manage | 39 ++++++++++++-------------------- nova/api/ec2/admin.py | 1 + nova/api/openstack/flavors.py | 2 ++ nova/compute/api.py | 2 ++ nova/compute/instance_types.py | 17 ++++---------- nova/db/api.py | 20 ++++++++++++++++ nova/db/sqlalchemy/api.py | 31 +++++++++++++++++++++++++ nova/db/sqlalchemy/models.py | 1 + nova/tests/api/openstack/test_flavors.py | 30 +++++++++++++++--------- nova/tests/db/fakes.py | 1 + nova/tests/test_quota.py | 3 +++ nova/tests/test_xenapi.py | 1 + nova/virt/libvirt_conn.py | 2 ++ nova/virt/xenapi/vm_utils.py | 1 + 14 files changed, 102 insertions(+), 49 deletions(-) diff --git a/bin/nova-manage b/bin/nova-manage index 952bf4fd1..0406a2dd9 100755 --- a/bin/nova-manage +++ b/bin/nova-manage @@ -611,40 +611,29 @@ class VolumeCommands(object): class InstanceTypesCommands(object): """Class for managing instance types / flavors.""" + def usage(self): + print "$ nova-manage instance_type NAME MEMORY_MB VCPUS LOCAL_GB" - def create(self, name, memory, vcpus, localstorage): + def create(self, name, memory, vcpus, local_gb): """Creates instance types / flavors - arguments: name memory vcpus localstorage""" - #for address in IPy.IP(range): - # db.floating_ip_create(context.get_admin_context(), - # {'address': str(address), - # 'host': host}) + arguments: name memory_mb vcpus local_gb""" + db.instance_type_create(context.get_admin_context(), name, memory, vcpus, local_gb) def delete(self, name): - """Deletes instance types / flavors + """Marks instance types / flavors as deleted arguments: name""" - #for address in IPy.IP(ip_range): - # db.floating_ip_destroy(context.get_admin_context(), - # str(address)) + ctxt = context.get_admin_context() + # check to see if it exists + db.instance_type_delete(context,name) def list(self): """Lists all instance types / flavors arguments: """ - #ctxt = context.get_admin_context() - #if host == None: - # floating_ips = db.floating_ip_get_all(ctxt) - #else: - # floating_ips = db.floating_ip_get_all_by_host(ctxt, host) - #for floating_ip in floating_ips: - # instance = None - # if floating_ip['fixed_ip']: - # instance = floating_ip['fixed_ip']['instance']['ec2_id'] - # print "%s\t%s\t%s" % (floating_ip['host'], - # floating_ip['address'], - # instance) - # print "%-10s %-10s %-8s %s %s" % (svc['host'], svc['binary'], - # active, art, - # svc['updated_at']) + instance_types = db.instance_type_get_all(context.get_admin_context()) + for instance in instance_types: + print "%s : %s memory (MB), %s vcpus, %s storage(GB)" % (instance.name, + instance.memory_mb, instance.vcpus, + instance.local_gb) CATEGORIES = [ diff --git a/nova/api/ec2/admin.py b/nova/api/ec2/admin.py index d7e899d12..55cca1041 100644 --- a/nova/api/ec2/admin.py +++ b/nova/api/ec2/admin.py @@ -79,6 +79,7 @@ class AdminController(object): def __str__(self): return 'AdminController' + # FIX-ME(kpepple) for dynamic flavors def describe_instance_types(self, _context, **_kwargs): return {'instanceTypeSet': [instance_dict(n, v) for n, v in instance_types.INSTANCE_TYPES.iteritems()]} diff --git a/nova/api/openstack/flavors.py b/nova/api/openstack/flavors.py index f620d4107..1f5185134 100644 --- a/nova/api/openstack/flavors.py +++ b/nova/api/openstack/flavors.py @@ -45,6 +45,7 @@ class Controller(wsgi.Controller): def show(self, req, id): """Return data about the given flavor id.""" + # FIX-ME(kpepple) for dynamic flavors for name, val in instance_types.INSTANCE_TYPES.iteritems(): if val['flavorid'] == int(id): item = dict(ram=val['memory_mb'], disk=val['local_gb'], @@ -54,4 +55,5 @@ class Controller(wsgi.Controller): def _all_ids(self): """Return the list of all flavorids.""" + # FIX-ME(kpepple) for dynamic flavors return [i['flavorid'] for i in instance_types.INSTANCE_TYPES.values()] diff --git a/nova/compute/api.py b/nova/compute/api.py index 1d8b9d79f..3ae722226 100644 --- a/nova/compute/api.py +++ b/nova/compute/api.py @@ -89,6 +89,8 @@ class API(base.Base): """Create the number of instances requested if quota and other arguments check out ok.""" + # FIXME(kpepple) this needs to be changed from using the old constant + #type_data = db.instance_type_get_by_name(context.get_admin_context(), instance_type) type_data = instance_types.INSTANCE_TYPES[instance_type] num_instances = quota.allowed_instances(context, max_count, type_data) if num_instances < min_count: diff --git a/nova/compute/instance_types.py b/nova/compute/instance_types.py index 1b3d0d0eb..0594aa063 100644 --- a/nova/compute/instance_types.py +++ b/nova/compute/instance_types.py @@ -25,6 +25,7 @@ from nova import flags from nova import exception FLAGS = flags.FLAGS +# FIX-ME(kpepple) for dynamic flavors INSTANCE_TYPES = { 'm1.tiny': dict(memory_mb=512, vcpus=1, local_gb=0, flavorid=1), 'm1.small': dict(memory_mb=2048, vcpus=1, local_gb=20, flavorid=2), @@ -35,8 +36,9 @@ INSTANCE_TYPES = { def get_by_type(instance_type): """Build instance data structure and save it to the data store.""" + # FIX-ME(kpepple) for dynamic flavors if instance_type is None: - return FLAGS.default_instance_type + return FLAGS.default_instance_type if instance_type not in INSTANCE_TYPES: raise exception.ApiError(_("Unknown instance type: %s"), instance_type) @@ -44,19 +46,8 @@ def get_by_type(instance_type): def get_by_flavor_id(flavor_id): + # FIX-ME(kpepple) for dynamic flavors for instance_type, details in INSTANCE_TYPES.iteritems(): if details['flavorid'] == flavor_id: return instance_type return FLAGS.default_instance_type - - -def list_flavors(): - return instance_type - - -def create_flavor(): - return instance_type - - -def delete_flavor(): - return instance_type diff --git a/nova/db/api.py b/nova/db/api.py index c6c03fb0e..ffb1d08ce 100644 --- a/nova/db/api.py +++ b/nova/db/api.py @@ -985,3 +985,23 @@ def console_get_all_by_instance(context, instance_id): def console_get(context, console_id, instance_id=None): """Get a specific console (possibly on a given instance).""" return IMPL.console_get(context, console_id, instance_id) + + + ################## + + +def instance_type_create(context, values): + """Create a new instance type""" + return IMPL.instance_type_create(context, values) + +def instance_type_get_all(context): + """Get all instance types""" + return IMPL.instance_type_get_all(context) + +def instance_type_get_by_name(context, name): + """Get instance type by name""" + return IMPL.instance_type_get_by_name(context, name) + +def instance_type_destroy(context, name): + """Delete a instance type""" + return IMPL.instance_type_destroy(context,name) diff --git a/nova/db/sqlalchemy/api.py b/nova/db/sqlalchemy/api.py index fa060228f..aa6434b65 100644 --- a/nova/db/sqlalchemy/api.py +++ b/nova/db/sqlalchemy/api.py @@ -2018,3 +2018,34 @@ def console_get(context, console_id, instance_id=None): raise exception.NotFound(_("No console with id %(console_id)s" " %(idesc)s") % locals()) return result + + + ################## + + +@require_admin_context +def instance_type_create(context, values): + instance_type_ref = models.InstanceTypes() + instance_type_ref.update(values) + instance_type_ref.save() + return instance_type_ref + +def instance_type_get_all(context): + session = get_session() + return session.query(models.InstanceTypes).\ + filter_by(deleted=0) + +def instance_type_get_by_name(context, name): + session = get_session() + return session.query(models.InstanceTypes).\ + filter_by(name=name).\ + first() + +@require_admin_context +def instance_type_destroy(context,name): + session = get_session() + instance_type_ref = session.query(models.InstanceTypes).\ + filter_by(name=name) + rows = instance_type_ref.update(dict(deleted=1)) + return instance_type_ref + diff --git a/nova/db/sqlalchemy/models.py b/nova/db/sqlalchemy/models.py index 762425670..44583861b 100644 --- a/nova/db/sqlalchemy/models.py +++ b/nova/db/sqlalchemy/models.py @@ -214,6 +214,7 @@ class InstanceTypes(BASE, NovaBase): """Represent possible instance_types or flavor of VM offered""" __tablename__ = "instance_types" id = Column(Integer, primary_key=True) + name = Column(String(255), unique=True) memory_mb = Column(Integer) vcpus = Column(Integer) local_gb = Column(Integer) diff --git a/nova/tests/api/openstack/test_flavors.py b/nova/tests/api/openstack/test_flavors.py index 05624c5a9..0f0ed2e84 100644 --- a/nova/tests/api/openstack/test_flavors.py +++ b/nova/tests/api/openstack/test_flavors.py @@ -21,6 +21,8 @@ import stubout import webob import nova.api +from nova import context +from nova import db from nova.api.openstack import flavors from nova.tests.api.openstack import fakes @@ -33,6 +35,7 @@ class FlavorsTest(unittest.TestCase): fakes.stub_out_networking(self.stubs) fakes.stub_out_rate_limiting(self.stubs) fakes.stub_out_auth(self.stubs) + self.context = context.get_admin_context() def tearDown(self): self.stubs.UnsetAll() @@ -41,17 +44,22 @@ class FlavorsTest(unittest.TestCase): req = webob.Request.blank('/v1.0/flavors') res = req.get_response(fakes.wsgi_app()) - def test_get_flavor_by_id(self): - pass - - def test_create_favor(self): - pass - - def test_delete_flavor(self): - pass - - def test_list_flavors(self): - pass + def test_create_list_delete_favor(self): + # create a new flavor + starting_flavors = db.instance_type_get_all(self.context) + new_instance_type = dict(name="os1.big",memory_mb=512, vcpus=1, local_gb=120, flavorid=25) + new_flavor = db.instance_type_create(self.context, new_instance_type) + self.assertEqual(new_flavor["name"], new_instance_type["name"]) + # retrieve the newly created flavor + retrieved_new_flavor = db.instance_type_get_by_name(self.context, new_instance_type["name"]) + # self.assertEqual(len(tuple(retrieved_new_flavor)),1) + self.assertEqual(retrieved_new_flavor["memory_mb"], new_instance_type["memory_mb"]) + flavors = db.instance_type_get_all(self.context) + self.assertNotEqual(starting_flavors, flavors) + # delete the newly created flavor + delete_query = db.instance_type_destroy(self.context,new_instance_type["name"]) + retrieve_deleted_flavor = db.instance_type_get_by_name(self.context, new_instance_type["name"]) + self.assertEqual(retrieve_deleted_flavor["deleted"], 1) if __name__ == '__main__': diff --git a/nova/tests/db/fakes.py b/nova/tests/db/fakes.py index 05bdd172e..6cf917d76 100644 --- a/nova/tests/db/fakes.py +++ b/nova/tests/db/fakes.py @@ -44,6 +44,7 @@ def stub_out_db_instance_api(stubs): def fake_instance_create(values): """ Stubs out the db.instance_create method """ + # FIX-ME(kpepple) for dynamic flavors type_data = instance_types.INSTANCE_TYPES[values['instance_type']] base_options = { diff --git a/nova/tests/test_quota.py b/nova/tests/test_quota.py index 9548a8c13..f2c9c456f 100644 --- a/nova/tests/test_quota.py +++ b/nova/tests/test_quota.py @@ -75,15 +75,18 @@ class QuotaTestCase(test.TestCase): def test_quota_overrides(self): """Make sure overriding a projects quotas works""" + # FIX-ME(kpepple) for dynamic flavors num_instances = quota.allowed_instances(self.context, 100, instance_types.INSTANCE_TYPES['m1.small']) self.assertEqual(num_instances, 2) db.quota_create(self.context, {'project_id': self.project.id, 'instances': 10}) + # FIX-ME(kpepple) for dynamic flavors num_instances = quota.allowed_instances(self.context, 100, instance_types.INSTANCE_TYPES['m1.small']) self.assertEqual(num_instances, 4) db.quota_update(self.context, self.project.id, {'cores': 100}) + # FIX-ME(kpepple) for dynamic flavors num_instances = quota.allowed_instances(self.context, 100, instance_types.INSTANCE_TYPES['m1.small']) self.assertEqual(num_instances, 10) diff --git a/nova/tests/test_xenapi.py b/nova/tests/test_xenapi.py index 9f5b266f3..e38bd4dab 100644 --- a/nova/tests/test_xenapi.py +++ b/nova/tests/test_xenapi.py @@ -225,6 +225,7 @@ class XenAPIVMTestCase(test.TestCase): vm = vms[0] # Check that m1.large above turned into the right thing. + # FIX-ME(kpepple) for dynamic flavors instance_type = instance_types.INSTANCE_TYPES['m1.large'] mem_kib = long(instance_type['memory_mb']) << 10 mem_bytes = str(mem_kib << 10) diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py index bd5c9c4ee..9ed6d4a71 100644 --- a/nova/virt/libvirt_conn.py +++ b/nova/virt/libvirt_conn.py @@ -611,6 +611,7 @@ class LibvirtConnection(object): user=user, project=project, size=size) + # FIX-ME(kpepple) for dynamic flavors type_data = instance_types.INSTANCE_TYPES[inst['instance_type']] if type_data['local_gb']: @@ -671,6 +672,7 @@ class LibvirtConnection(object): network = db.network_get_by_instance(context.get_admin_context(), instance['id']) # FIXME(vish): stick this in db + # FIX-ME(kpepple) for dynamic flavors instance_type = instance['instance_type'] instance_type = instance_types.INSTANCE_TYPES[instance_type] ip_address = db.instance_get_fixed_address(context.get_admin_context(), diff --git a/nova/virt/xenapi/vm_utils.py b/nova/virt/xenapi/vm_utils.py index 4afd28dd8..4d09b2afa 100644 --- a/nova/virt/xenapi/vm_utils.py +++ b/nova/virt/xenapi/vm_utils.py @@ -82,6 +82,7 @@ class VMHelper(HelperBase): the pv_kernel flag indicates whether the guest is HVM or PV """ + # FIX-ME(kpepple) for dynamic flavors instance_type = instance_types.INSTANCE_TYPES[instance.instance_type] mem = str(long(instance_type['memory_mb']) * 1024 * 1024) vcpus = str(instance_type['vcpus']) -- cgit From b65e994d9597f0a989b30eafc7a51bc34c4c361f Mon Sep 17 00:00:00 2001 From: Cerberus Date: Fri, 4 Feb 2011 15:13:18 -0600 Subject: Added a bunch of stubbed out functionality --- nova/compute/api.py | 22 +++++--- nova/compute/manager.py | 65 +++++++++++++++++++--- nova/db/api.py | 22 +++++++- nova/db/sqlalchemy/api.py | 53 ++++++++++++++++++ nova/db/sqlalchemy/models.py | 12 +++- nova/virt/xenapi/vmops.py | 18 +++++- nova/virt/xenapi_conn.py | 11 ++++ plugins/xenserver/xenapi/etc/xapi.d/plugins/glance | 4 ++ 8 files changed, 187 insertions(+), 20 deletions(-) diff --git a/nova/compute/api.py b/nova/compute/api.py index 283845709..f0d5ff2cb 100644 --- a/nova/compute/api.py +++ b/nova/compute/api.py @@ -379,6 +379,10 @@ class API(base.Base): kwargs = {'method': method, 'args': params} return rpc.call(context, queue, kwargs) + def _cast_scheduler_message(self, context, args) + """Generic handler for RPC calls to the scheduler""" + rpc.cast(context, FLAGS.scheduler_topic, args) + def snapshot(self, context, instance_id, name): """Snapshot the given instance. @@ -397,21 +401,23 @@ class API(base.Base): def revert_resize(self, context, instance_id): """Reverts a resize, deleting the 'new' instance in the process""" - raise NotImplemented() + instance_ref = self.db.instance_get(instance_id) + self._cast_compute_message('revert_resize', context, instance_id, + instance_ref['host']) def confirm_resize(self, context, instance_id): """Confirms a migration/resize, deleting the 'old' instance in the process.""" - raise NotImplemented() + migration_ref = self.db.get_migration_by_instance_id(instance_id) + self._cast_compute_message('confirm_resize', context, instance_id, + migration_ref['source_host']) def resize(self, context, instance_id, flavor): """Resize a running instance.""" - rpc.cast(context, - FLAGS.scheduler_topic, - {"method": "resize_instance", - "args": {"topic": FLAGS.compute_topic, - "instance_id": instance_id, - "flavor": flavor}}) + self._cast_scheduler_message(context, + {"method": "prep_resize", + "args": {"topic": FLAGS.compute_topic, + "instance_id": instance_id, }},) def pause(self, context, instance_id): """Pause the given instance.""" diff --git a/nova/compute/manager.py b/nova/compute/manager.py index 41ef23980..140db0d3e 100644 --- a/nova/compute/manager.py +++ b/nova/compute/manager.py @@ -380,18 +380,67 @@ class ComputeManager(manager.Manager): """Update instance state when async task completes.""" self._update_state(context, instance_id) + @exception.wrap_exception @checks_instance_lock - def resize_instance(self, context, instance_id, flavor_id): - """Moves a running instance to another host, possibly changing the RAM - and disk size in the process""" + def prep_resize(self, context, instance_id): + """Initiates the process of moving a running instance to another + host, possibly changing the RAM and disk size in the process""" context = context.elevated() instance_ref = self.db.instance_get(context. instance_id) - LOG.audit(_('instance %s: migrating'), instance_id, context=context) - self.db.instance_set_state(context, instance_id, power_state.RUNNING, - 'migrating') - self.driver.resize(instance_ref, flavor_id) - self._update_state(context, instance_id) + migration_ref = self.db.migration_create(context, + { 'instance_id': instance_id, + 'source_host': instance_ref['host'], + 'dest_host': socket.gethostbyname(socket.gethostname()), + 'status': 'pre-migrating' } + LOG.audit(_('instance %s: migrating to '), instance_id, context=context) + service = self.db.service_get_by_host_and_topic(context, + instance_ref['host'], topic) + topic = self.db.queue_get_for(context, topic, service['id']) + rpc.cast(context, topic, + { 'method': 'resize_instance', + 'migration_id': migration_ref['id'], } + + @exception.wrap_exception + @checks_instance_lock + def resize_instance(self, context, migration_id): + """Starts the migration of a running instance to another host""" + migration_ref = self.db.migration_get(context, migration_id) + self.db.migration_update(context, migration_id, + { 'status': 'migrating', }) + self.driver.transfer_disk(context, instance_id, + migration_ref['dest_host']) + self.db.migration_update(context, migration_id, + { 'status': 'post-migrating', }) + + self.driver.power_off(context, migration_ref['instance_id']) + # This is where we would update the VM record after resizing + + service = self.db.service_get_by_host_and_topic(context, + migration_ref['dest_host'], topic) + topic = self.db.queue_get_for(context, topic, service['id']) + rpc.cast(context, topic, + { 'method': 'finish_resize', + 'migration_id': migration_ref['id'], } + + @exception.wrap_exception + @checks_instance_lock + def finish_resize(self, context, migration_id): + """Completes the migration process by setting up the newly transferred + disk and turning on the instance on its new host machine""" + migration_ref = self.db.migration_get(context, migration_id) + instance_ref = self.db.instance_get(context, + migration_ref['instance_id']) + + # this may get passed into the following spawn instead + self.driver.attach_disk(context, migration_ref['instance_id']) + self.driver.spawn(context, instance_ref, preexisting=True) + + self.db.migration_update(context, migration_id, + {'status': 'finished', }) + + # Cleans up any transferred files and unmounts things + self.driver.cleanup_disk_transfer(context, instance_ref['id']) @exception.wrap_exception @checks_instance_lock diff --git a/nova/db/api.py b/nova/db/api.py index 789cb8ebb..5da0e9840 100644 --- a/nova/db/api.py +++ b/nova/db/api.py @@ -80,10 +80,15 @@ def service_destroy(context, instance_id): def service_get(context, service_id): - """Get an service or raise if it does not exist.""" + """Get a service or raise if it does not exist.""" return IMPL.service_get(context, service_id) +def service_get_by_host_and_topic(context, host, topic): + """Get a service by host it's on and topic it listens to""" + return IMPL.service_get(context, host, topic) + + def service_get_all(context, disabled=False): """Get all service.""" return IMPL.service_get_all(context, None, disabled) @@ -255,6 +260,21 @@ def floating_ip_get_by_address(context, address): #################### +def migration_create(context, values): + """Create a migration record""" + return IMPL.migration_create(context, values) + +def migration_get(context, migration_id): + """Finds a migration by the id""" + return IMPL.migration_get(context, migration_id) + +def migration_get_by_instance_id(context, instance_id): + """Finds a migration by the instance id its migrating""" + return IMPL.migration_get_by_instance_id(context, instance_id) + +#################### + + def fixed_ip_associate(context, address, instance_id): """Associate fixed ip to instance. diff --git a/nova/db/sqlalchemy/api.py b/nova/db/sqlalchemy/api.py index 85250d56e..e94f9f4d2 100644 --- a/nova/db/sqlalchemy/api.py +++ b/nova/db/sqlalchemy/api.py @@ -156,6 +156,15 @@ def service_get_all_by_topic(context, topic): filter_by(topic=topic).\ all() +@require_admin_context +def service_get_by_host_and_topic(context, host, topic): + session = get_session() + return session.query(models.Service).\ + filter_by(deleted=False).\ + filter_by(disabled=False).\ + filter_by(host=host).\ + filter_by(topic=topic).\ + all() @require_admin_context def service_get_all_by_host(context, host): @@ -1909,6 +1918,50 @@ def host_get_networks(context, host): all() +################### + + +@require_admin_context +def migration_create(context, values): + migration = models.Migration() + migration.update(values) + migration.save() + return migration + + +@require_admin_context +def migration_update(context, migration_id, values): + session = get_session() + with session.begin(): + migration = migration_get(context, migration_id, session=session) + migration.update(values) + return migration + + +@require_admin_context +def migration_get(context, migration_id): + session = get_session() + result = session.query(models.Migration.\ + filter_by(migration_id=migration_id)). + first() + if not result: + raise exception.NotFound(_("No migration found with id %s") + % migration_id) + return result + + +@require_admin_context +def migration_get_by_instance(context, instance_id): + session = get_session() + result = session.query(models.Migration.\ + filter_by(instance_id=instance_id)). + first() + if not result: + raise exception.NotFound(_("No migration found with instance id %s") + % migration_id) + return result + + ################## diff --git a/nova/db/sqlalchemy/models.py b/nova/db/sqlalchemy/models.py index 7efb36c0e..499275504 100644 --- a/nova/db/sqlalchemy/models.py +++ b/nova/db/sqlalchemy/models.py @@ -366,6 +366,15 @@ class KeyPair(BASE, NovaBase): public_key = Column(Text) +class Migration(BASE, NovaBase): + """Represents a running host-to-host migration.""" + __tablename__ = 'migrations' + source_host = Column(String(255)) + dest_host = Column(String(255)) + instance_id = Column(Integer, ForeignKey('instances.id'), nullable=True) + status = Column(String(255)) #TODO(_cerberus_): enum + + class Network(BASE, NovaBase): """Represents a network.""" __tablename__ = 'networks' @@ -547,7 +556,8 @@ def register_models(): Volume, ExportDevice, IscsiTarget, FixedIp, FloatingIp, Network, SecurityGroup, SecurityGroupIngressRule, SecurityGroupInstanceAssociation, AuthToken, User, - Project, Certificate, ConsolePool, Console) # , Image, Host + Project, Certificate, ConsolePool, Console, + Migration) # , Image, Host engine = create_engine(FLAGS.sql_connection, echo=False) for model in models: model.metadata.create_all(engine) diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py index 3f9eb39d1..468881355 100644 --- a/nova/virt/xenapi/vmops.py +++ b/nova/virt/xenapi/vmops.py @@ -60,6 +60,15 @@ class VMOps(object): vms.append(rec["name_label"]) return vms + def power_on(self, instance): + """Power on a VM instance""" + vm = VMHelper.lookup(self._session, instance.name) + if vm is None: + raise exception(_('Attempted to power on non-existent instance' + ' bad instance id %s') % instance.id) + LOG.debug(_("Starting instance %s"), instance.name) + self._session.call_xenapi('VM.start', vm, False, False) + def spawn(self, instance): """Create VM instance""" vm = VMHelper.lookup(self._session, instance.name) @@ -259,7 +268,8 @@ class VMOps(object): raise RuntimeError(resp_dict['message']) return resp_dict['message'] - def _shutdown(self, instance, vm): + + def _shutdown(self, instance, vm, method='hard'): """Shutdown an instance """ state = self.get_info(instance['name'])['state'] if state == power_state.SHUTDOWN: @@ -268,7 +278,11 @@ class VMOps(object): return try: - task = self._session.call_xenapi('Async.VM.hard_shutdown', vm) + task = None + if method == 'clean': + task = self._session.call_xenapi('Async.VM.clean_shutdown', vm) + else: + task = self._session.call_xenapi('Async.VM.hard_shutdown', vm) self._session.wait_for_task(instance.id, task) except self.XenAPI.Failure, exc: LOG.exception(exc) diff --git a/nova/virt/xenapi_conn.py b/nova/virt/xenapi_conn.py index 4637b4c29..628291764 100644 --- a/nova/virt/xenapi_conn.py +++ b/nova/virt/xenapi_conn.py @@ -184,6 +184,17 @@ class XenAPIConnection(object): """Unpause paused VM instance""" self._vmops.unpause(instance, callback) + def power_off(self, instance): + """Shuts down a running VM instance""" + self._vmops._shutdown(instance, method='clean') + + def power_on(self, instance): + """powers on a powered off VM instance""" + self._vmops.power_on(instance) + + def transfer_disk(self, instance, dest, callback): + self._vmops.transfer_disk( + def suspend(self, instance, callback): """suspend the specified instance""" self._vmops.suspend(instance, callback) diff --git a/plugins/xenserver/xenapi/etc/xapi.d/plugins/glance b/plugins/xenserver/xenapi/etc/xapi.d/plugins/glance index aadacce57..3b7ceacc9 100644 --- a/plugins/xenserver/xenapi/etc/xapi.d/plugins/glance +++ b/plugins/xenserver/xenapi/etc/xapi.d/plugins/glance @@ -54,6 +54,10 @@ def copy_kernel_vdi(session,args): _copy_kernel_vdi('/dev/%s' % dev,copy_args)) return filename +def transfer_disk(dest, args): + vdi = exists(args, 'vdi-ref') + + def _copy_kernel_vdi(dest,copy_args): vdi_uuid=copy_args['vdi_uuid'] vdi_size=copy_args['vdi_size'] -- cgit From 855b9443cf109302e9882d527f237049b9624a05 Mon Sep 17 00:00:00 2001 From: Cerberus Date: Fri, 4 Feb 2011 15:43:41 -0600 Subject: Didn't mean to actually make changes to the glance plugin --- plugins/xenserver/xenapi/etc/xapi.d/plugins/glance | 4 ---- 1 file changed, 4 deletions(-) diff --git a/plugins/xenserver/xenapi/etc/xapi.d/plugins/glance b/plugins/xenserver/xenapi/etc/xapi.d/plugins/glance index 3b7ceacc9..aadacce57 100644 --- a/plugins/xenserver/xenapi/etc/xapi.d/plugins/glance +++ b/plugins/xenserver/xenapi/etc/xapi.d/plugins/glance @@ -54,10 +54,6 @@ def copy_kernel_vdi(session,args): _copy_kernel_vdi('/dev/%s' % dev,copy_args)) return filename -def transfer_disk(dest, args): - vdi = exists(args, 'vdi-ref') - - def _copy_kernel_vdi(dest,copy_args): vdi_uuid=copy_args['vdi_uuid'] vdi_size=copy_args['vdi_size'] -- cgit From 25a5afbb783e28bd5303853bf09e4b254c938302 Mon Sep 17 00:00:00 2001 From: Ken Pepple Date: Sat, 5 Feb 2011 01:14:45 -0800 Subject: added FIXME(kpepple) comments for all constant usage of INSTANCE_TYPES. updated api/ec2/admin.py to use the new instance_types db table --- bin/nova-manage | 11 ++++++----- nova/api/ec2/admin.py | 19 ++++++++++++++----- nova/api/openstack/flavors.py | 4 ++-- nova/compute/api.py | 3 ++- nova/compute/instance_types.py | 8 ++++---- nova/db/api.py | 5 ++++- nova/db/sqlalchemy/api.py | 9 ++++++--- .../db/sqlalchemy/migrate_repo/versions/003_cactus.py | 2 +- nova/tests/api/openstack/test_flavors.py | 18 ++++++++++++------ nova/tests/db/fakes.py | 2 +- nova/tests/test_quota.py | 6 +++--- nova/tests/test_xenapi.py | 2 +- nova/virt/libvirt_conn.py | 4 ++-- nova/virt/xenapi/vm_utils.py | 2 +- 14 files changed, 59 insertions(+), 36 deletions(-) diff --git a/bin/nova-manage b/bin/nova-manage index 0406a2dd9..5a4875907 100755 --- a/bin/nova-manage +++ b/bin/nova-manage @@ -617,23 +617,24 @@ class InstanceTypesCommands(object): def create(self, name, memory, vcpus, local_gb): """Creates instance types / flavors arguments: name memory_mb vcpus local_gb""" - db.instance_type_create(context.get_admin_context(), name, memory, vcpus, local_gb) + db.instance_type_create(context.get_admin_context(), + name, memory, vcpus, local_gb) def delete(self, name): """Marks instance types / flavors as deleted arguments: name""" ctxt = context.get_admin_context() # check to see if it exists - db.instance_type_delete(context,name) + db.instance_type_delete(context, name) def list(self): """Lists all instance types / flavors arguments: """ instance_types = db.instance_type_get_all(context.get_admin_context()) for instance in instance_types: - print "%s : %s memory (MB), %s vcpus, %s storage(GB)" % (instance.name, - instance.memory_mb, instance.vcpus, - instance.local_gb) + print "%s : %s memory (MB), %s vcpus, %s storage(GB)" % + (instance.name, instance.memory_mb, instance.vcpus, + instance.local_gb) CATEGORIES = [ diff --git a/nova/api/ec2/admin.py b/nova/api/ec2/admin.py index 55cca1041..5a9e22bf7 100644 --- a/nova/api/ec2/admin.py +++ b/nova/api/ec2/admin.py @@ -63,8 +63,8 @@ def host_dict(host): return {} -def instance_dict(name, inst): - return {'name': name, +def instance_dict(inst): + return {'name': inst['name'], 'memory_mb': inst['memory_mb'], 'vcpus': inst['vcpus'], 'disk_gb': inst['local_gb'], @@ -79,10 +79,19 @@ class AdminController(object): def __str__(self): return 'AdminController' - # FIX-ME(kpepple) for dynamic flavors + # FIXME(kpepple) this is untested code path. def describe_instance_types(self, _context, **_kwargs): - return {'instanceTypeSet': [instance_dict(n, v) for n, v in - instance_types.INSTANCE_TYPES.iteritems()]} + """Returns all active instance types data (vcpus, memory, etc.)""" + # return {'instanceTypeSet': [instance_dict(n, v) for n, v in + # instance_types.INSTANCE_TYPES.iteritems()]} + return {'instanceTypeSet': + [for i in db.instance_type_get_all(): instance_dict(i)]} + + # FIXME(kpepple) this is untested code path. + def describe_instance_type(self, _context, name, **_kwargs): + """Returns a specific active instance types data""" + return {'instanceTypeSet': + [instance_dict(db.instance_type_get_by_name(name))]} def describe_user(self, _context, name, **_kwargs): """Returns user data, including access and secret keys.""" diff --git a/nova/api/openstack/flavors.py b/nova/api/openstack/flavors.py index 1f5185134..2416088f9 100644 --- a/nova/api/openstack/flavors.py +++ b/nova/api/openstack/flavors.py @@ -45,7 +45,7 @@ class Controller(wsgi.Controller): def show(self, req, id): """Return data about the given flavor id.""" - # FIX-ME(kpepple) for dynamic flavors + # FIXME(kpepple) for dynamic flavors for name, val in instance_types.INSTANCE_TYPES.iteritems(): if val['flavorid'] == int(id): item = dict(ram=val['memory_mb'], disk=val['local_gb'], @@ -55,5 +55,5 @@ class Controller(wsgi.Controller): def _all_ids(self): """Return the list of all flavorids.""" - # FIX-ME(kpepple) for dynamic flavors + # FIXME(kpepple) for dynamic flavors return [i['flavorid'] for i in instance_types.INSTANCE_TYPES.values()] diff --git a/nova/compute/api.py b/nova/compute/api.py index 3ae722226..78a34dff2 100644 --- a/nova/compute/api.py +++ b/nova/compute/api.py @@ -90,7 +90,8 @@ class API(base.Base): other arguments check out ok.""" # FIXME(kpepple) this needs to be changed from using the old constant - #type_data = db.instance_type_get_by_name(context.get_admin_context(), instance_type) + #type_data = db.instance_type_get_by_name(context.get_admin_context(), + # instance_type) type_data = instance_types.INSTANCE_TYPES[instance_type] num_instances = quota.allowed_instances(context, max_count, type_data) if num_instances < min_count: diff --git a/nova/compute/instance_types.py b/nova/compute/instance_types.py index 0594aa063..449ec1d2e 100644 --- a/nova/compute/instance_types.py +++ b/nova/compute/instance_types.py @@ -25,7 +25,7 @@ from nova import flags from nova import exception FLAGS = flags.FLAGS -# FIX-ME(kpepple) for dynamic flavors +# FIXME(kpepple) for dynamic flavors INSTANCE_TYPES = { 'm1.tiny': dict(memory_mb=512, vcpus=1, local_gb=0, flavorid=1), 'm1.small': dict(memory_mb=2048, vcpus=1, local_gb=20, flavorid=2), @@ -36,9 +36,9 @@ INSTANCE_TYPES = { def get_by_type(instance_type): """Build instance data structure and save it to the data store.""" - # FIX-ME(kpepple) for dynamic flavors + # FIXME(kpepple) for dynamic flavors if instance_type is None: - return FLAGS.default_instance_type + return FLAGS.default_instance_type if instance_type not in INSTANCE_TYPES: raise exception.ApiError(_("Unknown instance type: %s"), instance_type) @@ -46,7 +46,7 @@ def get_by_type(instance_type): def get_by_flavor_id(flavor_id): - # FIX-ME(kpepple) for dynamic flavors + # FIXME(kpepple) for dynamic flavors for instance_type, details in INSTANCE_TYPES.iteritems(): if details['flavorid'] == flavor_id: return instance_type diff --git a/nova/db/api.py b/nova/db/api.py index ffb1d08ce..d5091794b 100644 --- a/nova/db/api.py +++ b/nova/db/api.py @@ -994,14 +994,17 @@ def instance_type_create(context, values): """Create a new instance type""" return IMPL.instance_type_create(context, values) + def instance_type_get_all(context): """Get all instance types""" return IMPL.instance_type_get_all(context) + def instance_type_get_by_name(context, name): """Get instance type by name""" return IMPL.instance_type_get_by_name(context, name) + def instance_type_destroy(context, name): """Delete a instance type""" - return IMPL.instance_type_destroy(context,name) + return IMPL.instance_type_destroy(context, name) diff --git a/nova/db/sqlalchemy/api.py b/nova/db/sqlalchemy/api.py index aa6434b65..142b1aa33 100644 --- a/nova/db/sqlalchemy/api.py +++ b/nova/db/sqlalchemy/api.py @@ -2030,10 +2030,13 @@ def instance_type_create(context, values): instance_type_ref.save() return instance_type_ref + def instance_type_get_all(context): session = get_session() return session.query(models.InstanceTypes).\ - filter_by(deleted=0) + filter_by(deleted=0).\ + all() + def instance_type_get_by_name(context, name): session = get_session() @@ -2041,11 +2044,11 @@ def instance_type_get_by_name(context, name): filter_by(name=name).\ first() + @require_admin_context -def instance_type_destroy(context,name): +def instance_type_destroy(context, name): session = get_session() instance_type_ref = session.query(models.InstanceTypes).\ filter_by(name=name) rows = instance_type_ref.update(dict(deleted=1)) return instance_type_ref - diff --git a/nova/db/sqlalchemy/migrate_repo/versions/003_cactus.py b/nova/db/sqlalchemy/migrate_repo/versions/003_cactus.py index c7e61dc05..6523b6b38 100644 --- a/nova/db/sqlalchemy/migrate_repo/versions/003_cactus.py +++ b/nova/db/sqlalchemy/migrate_repo/versions/003_cactus.py @@ -68,7 +68,7 @@ def upgrade(migrate_engine): # FIXME(kpepple) should we be seeding created_at / updated_at ? # the_time = time.strftime("%Y-%m-%d %H:%M:%S") i.execute({'name': name, 'memory_mb': values["memory_mb"], - 'vcpus': values["vcpus"], + 'vcpus': values["vcpus"], 'deleted': 0, 'local_gb': values["local_gb"], 'flavorid': values["flavorid"]}) except Exception: diff --git a/nova/tests/api/openstack/test_flavors.py b/nova/tests/api/openstack/test_flavors.py index 0f0ed2e84..b416e02d6 100644 --- a/nova/tests/api/openstack/test_flavors.py +++ b/nova/tests/api/openstack/test_flavors.py @@ -47,19 +47,25 @@ class FlavorsTest(unittest.TestCase): def test_create_list_delete_favor(self): # create a new flavor starting_flavors = db.instance_type_get_all(self.context) - new_instance_type = dict(name="os1.big",memory_mb=512, vcpus=1, local_gb=120, flavorid=25) + new_instance_type = dict(name="os1.big", memory_mb=512, + vcpus=1, local_gb=120, flavorid=25) new_flavor = db.instance_type_create(self.context, new_instance_type) self.assertEqual(new_flavor["name"], new_instance_type["name"]) # retrieve the newly created flavor - retrieved_new_flavor = db.instance_type_get_by_name(self.context, new_instance_type["name"]) + retrieved_new_flavor = db.instance_type_get_by_name( + self.context, + new_instance_type["name"]) # self.assertEqual(len(tuple(retrieved_new_flavor)),1) - self.assertEqual(retrieved_new_flavor["memory_mb"], new_instance_type["memory_mb"]) + self.assertEqual(retrieved_new_flavor["memory_mb"], + new_instance_type["memory_mb"]) flavors = db.instance_type_get_all(self.context) self.assertNotEqual(starting_flavors, flavors) # delete the newly created flavor - delete_query = db.instance_type_destroy(self.context,new_instance_type["name"]) - retrieve_deleted_flavor = db.instance_type_get_by_name(self.context, new_instance_type["name"]) - self.assertEqual(retrieve_deleted_flavor["deleted"], 1) + delete_query = db.instance_type_destroy(self.context, + new_instance_type["name"]) + deleted_flavor = db.instance_type_get_by_name(self.context, + new_instance_type["name"]) + self.assertEqual(deleted_flavor["deleted"], 1) if __name__ == '__main__': diff --git a/nova/tests/db/fakes.py b/nova/tests/db/fakes.py index 6cf917d76..3b47fc867 100644 --- a/nova/tests/db/fakes.py +++ b/nova/tests/db/fakes.py @@ -44,7 +44,7 @@ def stub_out_db_instance_api(stubs): def fake_instance_create(values): """ Stubs out the db.instance_create method """ - # FIX-ME(kpepple) for dynamic flavors + # FIXME(kpepple) for dynamic flavors type_data = instance_types.INSTANCE_TYPES[values['instance_type']] base_options = { diff --git a/nova/tests/test_quota.py b/nova/tests/test_quota.py index f2c9c456f..c70803e05 100644 --- a/nova/tests/test_quota.py +++ b/nova/tests/test_quota.py @@ -75,18 +75,18 @@ class QuotaTestCase(test.TestCase): def test_quota_overrides(self): """Make sure overriding a projects quotas works""" - # FIX-ME(kpepple) for dynamic flavors + # FIXME(kpepple) for dynamic flavors num_instances = quota.allowed_instances(self.context, 100, instance_types.INSTANCE_TYPES['m1.small']) self.assertEqual(num_instances, 2) db.quota_create(self.context, {'project_id': self.project.id, 'instances': 10}) - # FIX-ME(kpepple) for dynamic flavors + # FIXME(kpepple) for dynamic flavors num_instances = quota.allowed_instances(self.context, 100, instance_types.INSTANCE_TYPES['m1.small']) self.assertEqual(num_instances, 4) db.quota_update(self.context, self.project.id, {'cores': 100}) - # FIX-ME(kpepple) for dynamic flavors + # FIXME(kpepple) for dynamic flavors num_instances = quota.allowed_instances(self.context, 100, instance_types.INSTANCE_TYPES['m1.small']) self.assertEqual(num_instances, 10) diff --git a/nova/tests/test_xenapi.py b/nova/tests/test_xenapi.py index e38bd4dab..1d42f8da3 100644 --- a/nova/tests/test_xenapi.py +++ b/nova/tests/test_xenapi.py @@ -225,7 +225,7 @@ class XenAPIVMTestCase(test.TestCase): vm = vms[0] # Check that m1.large above turned into the right thing. - # FIX-ME(kpepple) for dynamic flavors + # FIXME(kpepple) for dynamic flavors instance_type = instance_types.INSTANCE_TYPES['m1.large'] mem_kib = long(instance_type['memory_mb']) << 10 mem_bytes = str(mem_kib << 10) diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py index 9ed6d4a71..e66115464 100644 --- a/nova/virt/libvirt_conn.py +++ b/nova/virt/libvirt_conn.py @@ -611,7 +611,7 @@ class LibvirtConnection(object): user=user, project=project, size=size) - # FIX-ME(kpepple) for dynamic flavors + # FIXME(kpepple) for dynamic flavors type_data = instance_types.INSTANCE_TYPES[inst['instance_type']] if type_data['local_gb']: @@ -672,7 +672,7 @@ class LibvirtConnection(object): network = db.network_get_by_instance(context.get_admin_context(), instance['id']) # FIXME(vish): stick this in db - # FIX-ME(kpepple) for dynamic flavors + # FIXME(kpepple) for dynamic flavors instance_type = instance['instance_type'] instance_type = instance_types.INSTANCE_TYPES[instance_type] ip_address = db.instance_get_fixed_address(context.get_admin_context(), diff --git a/nova/virt/xenapi/vm_utils.py b/nova/virt/xenapi/vm_utils.py index 4d09b2afa..3f0112609 100644 --- a/nova/virt/xenapi/vm_utils.py +++ b/nova/virt/xenapi/vm_utils.py @@ -82,7 +82,7 @@ class VMHelper(HelperBase): the pv_kernel flag indicates whether the guest is HVM or PV """ - # FIX-ME(kpepple) for dynamic flavors + # FIXME(kpepple) for dynamic flavors instance_type = instance_types.INSTANCE_TYPES[instance.instance_type] mem = str(long(instance_type['memory_mb']) * 1024 * 1024) vcpus = str(instance_type['vcpus']) -- cgit From 79ea4533df3bd8c58b96177c2979fab2987a842a Mon Sep 17 00:00:00 2001 From: Ken Pepple Date: Sat, 5 Feb 2011 02:45:53 -0800 Subject: converted openstack flavors over to use instance_types table. a few pep changes. --- bin/nova-manage | 5 ++--- nova/api/openstack/flavors.py | 22 ++++++++++++++-------- nova/db/api.py | 5 +++++ nova/db/sqlalchemy/api.py | 7 +++++++ .../sqlalchemy/migrate_repo/versions/003_cactus.py | 4 ++-- 5 files changed, 30 insertions(+), 13 deletions(-) diff --git a/bin/nova-manage b/bin/nova-manage index 5a4875907..69285a42a 100755 --- a/bin/nova-manage +++ b/bin/nova-manage @@ -632,9 +632,8 @@ class InstanceTypesCommands(object): arguments: """ instance_types = db.instance_type_get_all(context.get_admin_context()) for instance in instance_types: - print "%s : %s memory (MB), %s vcpus, %s storage(GB)" % - (instance.name, instance.memory_mb, instance.vcpus, - instance.local_gb) + print "%s : %s memory(MB), %s vcpus, %s storage(GB)" % (instance.name, + instance.memory_mb, instance.vcpus, instance.local_gb) CATEGORIES = [ diff --git a/nova/api/openstack/flavors.py b/nova/api/openstack/flavors.py index 2416088f9..7440af0b4 100644 --- a/nova/api/openstack/flavors.py +++ b/nova/api/openstack/flavors.py @@ -17,6 +17,8 @@ from webob import exc +from nova import db +from nova import context from nova.api.openstack import faults from nova.api.openstack import common from nova.compute import instance_types @@ -45,15 +47,19 @@ class Controller(wsgi.Controller): def show(self, req, id): """Return data about the given flavor id.""" - # FIXME(kpepple) for dynamic flavors - for name, val in instance_types.INSTANCE_TYPES.iteritems(): - if val['flavorid'] == int(id): - item = dict(ram=val['memory_mb'], disk=val['local_gb'], - id=val['flavorid'], name=name) - return dict(flavor=item) + # FIXME(kpepple) do we need admin context here ? + ctxt = context.get_admin_context() + val = db.instance_type_get_by_flavor_id(ctxt, id) + item = dict(ram=val['memory_mb'], disk=val['local_gb'], + id=val['flavorid'], name=val['name']) + return dict(flavor=item) raise faults.Fault(exc.HTTPNotFound()) def _all_ids(self): """Return the list of all flavorids.""" - # FIXME(kpepple) for dynamic flavors - return [i['flavorid'] for i in instance_types.INSTANCE_TYPES.values()] + # FIXME(kpepple) do we need admin context here ? + ctxt = context.get_admin_context() + flavor_ids = [] + for i in db.instance_type_get_all(ctxt): + flavor_ids.append(i['flavorid']) + return flavor_ids diff --git a/nova/db/api.py b/nova/db/api.py index d5091794b..2f1903ade 100644 --- a/nova/db/api.py +++ b/nova/db/api.py @@ -1005,6 +1005,11 @@ def instance_type_get_by_name(context, name): return IMPL.instance_type_get_by_name(context, name) +def instance_type_get_by_flavor_id(context, id): + """Get instance type by name""" + return IMPL.instance_type_get_by_flavor_id(context, id) + + def instance_type_destroy(context, name): """Delete a instance type""" return IMPL.instance_type_destroy(context, name) diff --git a/nova/db/sqlalchemy/api.py b/nova/db/sqlalchemy/api.py index 142b1aa33..6c9af6e19 100644 --- a/nova/db/sqlalchemy/api.py +++ b/nova/db/sqlalchemy/api.py @@ -2045,6 +2045,13 @@ def instance_type_get_by_name(context, name): first() +def instance_type_get_by_flavor_id(context, id): + session = get_session() + return session.query(models.InstanceTypes).\ + filter_by(flavorid=int(id)).\ + first() + + @require_admin_context def instance_type_destroy(context, name): session = get_session() diff --git a/nova/db/sqlalchemy/migrate_repo/versions/003_cactus.py b/nova/db/sqlalchemy/migrate_repo/versions/003_cactus.py index 6523b6b38..c5ae9ea72 100644 --- a/nova/db/sqlalchemy/migrate_repo/versions/003_cactus.py +++ b/nova/db/sqlalchemy/migrate_repo/versions/003_cactus.py @@ -19,7 +19,7 @@ from nova import api from nova import db from nova import log as logging -import time +import datetime meta = MetaData() @@ -66,7 +66,7 @@ def upgrade(migrate_engine): i = instance_types.insert() for name, values in INSTANCE_TYPES.iteritems(): # FIXME(kpepple) should we be seeding created_at / updated_at ? - # the_time = time.strftime("%Y-%m-%d %H:%M:%S") + # now = datetime.datatime.utcnow() i.execute({'name': name, 'memory_mb': values["memory_mb"], 'vcpus': values["vcpus"], 'deleted': 0, 'local_gb': values["local_gb"], -- cgit From fcd0a7b245470054718c94adf0da6a528a01f173 Mon Sep 17 00:00:00 2001 From: Ken Pepple Date: Sat, 5 Feb 2011 13:49:38 -0800 Subject: corrected db.instance_types to return expect dict instead of lists. updated openstack flavors to expect dicts instead of lists. added deleted column to returned dict. --- bin/nova-manage | 5 ++-- nova/api/openstack/flavors.py | 12 +++++---- nova/db/sqlalchemy/api.py | 42 ++++++++++++++++++++++++++++---- nova/tests/api/openstack/test_flavors.py | 5 ++-- 4 files changed, 49 insertions(+), 15 deletions(-) diff --git a/bin/nova-manage b/bin/nova-manage index 69285a42a..2db1c67bf 100755 --- a/bin/nova-manage +++ b/bin/nova-manage @@ -632,8 +632,9 @@ class InstanceTypesCommands(object): arguments: """ instance_types = db.instance_type_get_all(context.get_admin_context()) for instance in instance_types: - print "%s : %s memory(MB), %s vcpus, %s storage(GB)" % (instance.name, - instance.memory_mb, instance.vcpus, instance.local_gb) + print "%s : %s memory(MB), %s vcpus, %s storage(GB)" % \ + (instance.name, instance.memory_mb, instance.vcpus, + instance.local_gb) CATEGORIES = [ diff --git a/nova/api/openstack/flavors.py b/nova/api/openstack/flavors.py index 7440af0b4..da38dd34d 100644 --- a/nova/api/openstack/flavors.py +++ b/nova/api/openstack/flavors.py @@ -17,7 +17,7 @@ from webob import exc -from nova import db +from nova import db from nova import context from nova.api.openstack import faults from nova.api.openstack import common @@ -50,8 +50,9 @@ class Controller(wsgi.Controller): # FIXME(kpepple) do we need admin context here ? ctxt = context.get_admin_context() val = db.instance_type_get_by_flavor_id(ctxt, id) - item = dict(ram=val['memory_mb'], disk=val['local_gb'], - id=val['flavorid'], name=val['name']) + v = val.values()[0] + item = dict(ram=v['memory_mb'], disk=v['local_gb'], + id=v['flavorid'], name=val.keys()[0]) return dict(flavor=item) raise faults.Fault(exc.HTTPNotFound()) @@ -60,6 +61,7 @@ class Controller(wsgi.Controller): # FIXME(kpepple) do we need admin context here ? ctxt = context.get_admin_context() flavor_ids = [] - for i in db.instance_type_get_all(ctxt): - flavor_ids.append(i['flavorid']) + inst_types = db.instance_type_get_all(ctxt) + for i in inst_types.keys(): + flavor_ids.append(inst_types[i]['flavorid']) return flavor_ids diff --git a/nova/db/sqlalchemy/api.py b/nova/db/sqlalchemy/api.py index 6c9af6e19..83c88a3a0 100644 --- a/nova/db/sqlalchemy/api.py +++ b/nova/db/sqlalchemy/api.py @@ -2032,24 +2032,56 @@ def instance_type_create(context, values): def instance_type_get_all(context): + """Returns a dict of all instance_types: + { 'm1.tiny': dict(memory_mb=512, vcpus=1, local_gb=0, flavorid=1), + 'm1.small': dict(memory_mb=2048, vcpus=1, local_gb=20, flavorid=2), + 'm1.medium': dict(memory_mb=4096, vcpus=2, local_gb=40, flavorid=3)} + """ session = get_session() - return session.query(models.InstanceTypes).\ + inst_types = session.query(models.InstanceTypes).\ filter_by(deleted=0).\ all() + inst_dict = {} + for i in inst_types: + inst_dict[i['name']] = dict(memory_mb=i['memory_mb'], + vcpus=i['vcpus'], + local_gb=i['local_gb'], + flavorid=i['flavorid'], + deleted=i['deleted']) + + return inst_dict def instance_type_get_by_name(context, name): + """ + Returns a dict of specific instance_type: + {'m1.tiny': dict(memory_mb=512, vcpus=1, local_gb=0, flavorid=1)} + """ session = get_session() - return session.query(models.InstanceTypes).\ + inst_type = session.query(models.InstanceTypes).\ filter_by(name=name).\ first() + return {inst_type['name']: dict(memory_mb=inst_type['memory_mb'], + vcpus=inst_type['vcpus'], + local_gb=inst_type['local_gb'], + flavorid=inst_type['flavorid'], + deleted=inst_type['deleted'])} def instance_type_get_by_flavor_id(context, id): + """ + Returns a dict of specific flavor_id: + {'m1.tiny': dict(memory_mb=512, vcpus=1, local_gb=0, flavorid=1)} + """ session = get_session() - return session.query(models.InstanceTypes).\ - filter_by(flavorid=int(id)).\ - first() + inst_type = session.query(models.InstanceTypes).\ + filter_by(flavorid=int(id)).\ + first() + return {inst_type['name']: dict(memory_mb=inst_type['memory_mb'], + vcpus=inst_type['vcpus'], + local_gb=inst_type['local_gb'], + flavorid=inst_type['flavorid'], + deleted=inst_type['deleted'])} @require_admin_context diff --git a/nova/tests/api/openstack/test_flavors.py b/nova/tests/api/openstack/test_flavors.py index b416e02d6..9287e8adf 100644 --- a/nova/tests/api/openstack/test_flavors.py +++ b/nova/tests/api/openstack/test_flavors.py @@ -55,8 +55,7 @@ class FlavorsTest(unittest.TestCase): retrieved_new_flavor = db.instance_type_get_by_name( self.context, new_instance_type["name"]) - # self.assertEqual(len(tuple(retrieved_new_flavor)),1) - self.assertEqual(retrieved_new_flavor["memory_mb"], + self.assertEqual(retrieved_new_flavor.values()[0]["memory_mb"], new_instance_type["memory_mb"]) flavors = db.instance_type_get_all(self.context) self.assertNotEqual(starting_flavors, flavors) @@ -65,7 +64,7 @@ class FlavorsTest(unittest.TestCase): new_instance_type["name"]) deleted_flavor = db.instance_type_get_by_name(self.context, new_instance_type["name"]) - self.assertEqual(deleted_flavor["deleted"], 1) + self.assertEqual(deleted_flavor.values()[0]["deleted"], 1) if __name__ == '__main__': -- cgit From 5cd5d4e9682848cba60a8dec352fe0f74aaa9eac Mon Sep 17 00:00:00 2001 From: Ken Pepple Date: Sat, 5 Feb 2011 18:23:01 -0800 Subject: flavorid and name need to be unique in the database for the ec2 and openstack apis, repectively --- nova/db/sqlalchemy/migrate_repo/versions/003_cactus.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/nova/db/sqlalchemy/migrate_repo/versions/003_cactus.py b/nova/db/sqlalchemy/migrate_repo/versions/003_cactus.py index c5ae9ea72..f95996042 100644 --- a/nova/db/sqlalchemy/migrate_repo/versions/003_cactus.py +++ b/nova/db/sqlalchemy/migrate_repo/versions/003_cactus.py @@ -34,12 +34,13 @@ instance_types = Table('instance_types', meta, Column('deleted', Boolean(create_constraint=True, name=None)), Column('name', String(length=255, convert_unicode=False, assert_unicode=None, - unicode_error=None, _warn_on_bytestring=False)), + unicode_error=None, _warn_on_bytestring=False), + unique=True), Column('id', Integer(), primary_key=True, nullable=False), Column('memory_mb', Integer(), nullable=False), Column('vcpus', Integer(), nullable=False), Column('local_gb', Integer(), nullable=False), - Column('flavorid', Integer(), nullable=False), + Column('flavorid', Integer(), nullable=False, unique=True), ) -- cgit From d5a5324fee480152fd4e77f19fce5b025e1b4987 Mon Sep 17 00:00:00 2001 From: Ken Pepple Date: Sat, 5 Feb 2011 18:26:53 -0800 Subject: instance_types should return in predicatable order (by name currently) --- nova/db/sqlalchemy/api.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/nova/db/sqlalchemy/api.py b/nova/db/sqlalchemy/api.py index 83c88a3a0..0e2675a9f 100644 --- a/nova/db/sqlalchemy/api.py +++ b/nova/db/sqlalchemy/api.py @@ -2040,6 +2040,7 @@ def instance_type_get_all(context): session = get_session() inst_types = session.query(models.InstanceTypes).\ filter_by(deleted=0).\ + order_by("name").\ all() inst_dict = {} for i in inst_types: @@ -2090,4 +2091,4 @@ def instance_type_destroy(context, name): instance_type_ref = session.query(models.InstanceTypes).\ filter_by(name=name) rows = instance_type_ref.update(dict(deleted=1)) - return instance_type_ref + return rows -- cgit From 12a9db3e767b6b88fac5ad1d16c0aef39c4a801f Mon Sep 17 00:00:00 2001 From: Ken Pepple Date: Sat, 5 Feb 2011 18:27:51 -0800 Subject: rewrote nova-manage instance_type to use correct db.api returned objects and have more robust error handling --- bin/nova-manage | 47 ++++++++++++++++++++++++++++++++++------------- 1 file changed, 34 insertions(+), 13 deletions(-) diff --git a/bin/nova-manage b/bin/nova-manage index 2db1c67bf..8e2c7962f 100755 --- a/bin/nova-manage +++ b/bin/nova-manage @@ -611,30 +611,51 @@ class VolumeCommands(object): class InstanceTypesCommands(object): """Class for managing instance types / flavors.""" - def usage(self): - print "$ nova-manage instance_type NAME MEMORY_MB VCPUS LOCAL_GB" - def create(self, name, memory, vcpus, local_gb): + def create(self, name, memory, vcpus, local_gb, flavorid): """Creates instance types / flavors arguments: name memory_mb vcpus local_gb""" + # FIXME(kpepple) check for absurb arguments (?) + for option in [memory, flavorid, local_gb, vcpus]: + if option <= 0: + print "Instance type parameters must be positive \ + numbers: %s" % option + sys.exit(1) db.instance_type_create(context.get_admin_context(), - name, memory, vcpus, local_gb) + dict(name=name, memory_mb=memory, + vcpus=vcpus, local_gb=local_gb, + flavorid=flavorid)) + print "%s created" % name + return def delete(self, name): """Marks instance types / flavors as deleted arguments: name""" - ctxt = context.get_admin_context() - # check to see if it exists - db.instance_type_delete(context, name) + if name == None: + print "Instance type name must be supplied" + exit(1) + else: + records = db.instance_type_destroy(context.get_admin_context(),\ + name) + if records != 1: + sys.exit(1) + return - def list(self): + def list(self, name=None): """Lists all instance types / flavors - arguments: """ - instance_types = db.instance_type_get_all(context.get_admin_context()) - for instance in instance_types: + arguments: [name]""" + ctxt = context.get_admin_context() + if name == None: + instance_types = db.instance_type_get_all(ctxt) + if len(instance_types) < 1: + sys.exit(1) + else: + instance_types = db.instance_type_get_by_name(ctxt, name) + for k, v in instance_types.iteritems(): print "%s : %s memory(MB), %s vcpus, %s storage(GB)" % \ - (instance.name, instance.memory_mb, instance.vcpus, - instance.local_gb) + (k, v["memory_mb"], + v["vcpus"], v["local_gb"]) + return CATEGORIES = [ -- cgit From 2acc31a293b067644f26877dc52bacb7ef9e9bd1 Mon Sep 17 00:00:00 2001 From: Ken Pepple Date: Sat, 5 Feb 2011 18:28:26 -0800 Subject: added preliminary testing for bin/nova-manage while i am somewhat conflicted about the path these tests have taken, i think it is better than no tests at all --- nova/tests/test_nova_manage.py | 72 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 72 insertions(+) create mode 100644 nova/tests/test_nova_manage.py diff --git a/nova/tests/test_nova_manage.py b/nova/tests/test_nova_manage.py new file mode 100644 index 000000000..09ed8163b --- /dev/null +++ b/nova/tests/test_nova_manage.py @@ -0,0 +1,72 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# 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. +""" +Tests For Nova-Manage +""" + +import os +import subprocess + +from nova import test + + +class NovaManageTestCase(test.TestCase): + """Test case for nova-manage""" + def setUp(self): + super(NovaManageTestCase, self).setUp() + + def teardown(self): + fnull.close() + + def test_create_and_delete_instance_types(self): + fnull = open(os.devnull, 'w') + retcode = subprocess.call(["bin/nova-manage", "instance_type",\ + "create", "test", "256", "1",\ + "120", "99"], stdout=fnull) + self.assertEqual(0, retcode) + retcode = subprocess.call(["bin/nova-manage", "instance_type",\ + "delete", "test"], stdout=fnull) + self.assertEqual(0, retcode) + + def test_list_instance_types(self): + fnull = open(os.devnull, 'w') + retcode = subprocess.call(["bin/nova-manage", "instance_type", \ + "list"], stdout=fnull) + self.assertEqual(0, retcode) + + def test_list_specific_instance_type(self): + fnull = open(os.devnull, 'w') + retcode = subprocess.call(["bin/nova-manage", "instance_type", "list", + "m1.medium"], stdout=fnull) + self.assertEqual(0, retcode) + + def test_should_raise_on_bad_create_args(self): + fnull = open(os.devnull, 'w') + retcode = subprocess.call(["bin/nova-manage", "instance_type",\ + "create", "test", "256", "0",\ + "120", "99"], stdout=fnull) + self.assertEqual(1, retcode) + + def test_should_fail_on_duplicate_flavorid(self): + fnull = open(os.devnull, 'w') + retcode = subprocess.call(["bin/nova-manage", "instance_type",\ + "create", "test", "256", "1",\ + "120", "1"], stdout=fnull) + self.assertEqual(1, retcode) + + def test_instance_type_delete_should_fail_without_valid_name(self): + fnull = open(os.devnull, 'w') + retcode = subprocess.call(["bin/nova-manage", "instance_type",\ + "delete", "saefasff"], stdout=fnull) + self.assertEqual(1, retcode) -- cgit From 555e5b5a0d3ae30f5d8b77d6b2dc47a953b4a81b Mon Sep 17 00:00:00 2001 From: Ken Pepple Date: Sat, 5 Feb 2011 18:54:56 -0800 Subject: updated api.create to use instance_type table --- nova/compute/api.py | 7 +++---- nova/db/sqlalchemy/api.py | 2 ++ 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/nova/compute/api.py b/nova/compute/api.py index 78a34dff2..006db880e 100644 --- a/nova/compute/api.py +++ b/nova/compute/api.py @@ -89,10 +89,9 @@ class API(base.Base): """Create the number of instances requested if quota and other arguments check out ok.""" - # FIXME(kpepple) this needs to be changed from using the old constant - #type_data = db.instance_type_get_by_name(context.get_admin_context(), - # instance_type) - type_data = instance_types.INSTANCE_TYPES[instance_type] + # FIXME(kpepple) this needs to be factored for api.py:2065 refactor + type_data = db.instance_type_get_by_name(context,\ + instance_type)[instance_type] num_instances = quota.allowed_instances(context, max_count, type_data) if num_instances < min_count: pid = context.project_id diff --git a/nova/db/sqlalchemy/api.py b/nova/db/sqlalchemy/api.py index 0e2675a9f..eff03aa02 100644 --- a/nova/db/sqlalchemy/api.py +++ b/nova/db/sqlalchemy/api.py @@ -2062,6 +2062,7 @@ def instance_type_get_by_name(context, name): inst_type = session.query(models.InstanceTypes).\ filter_by(name=name).\ first() + # FIXME(kpepple) this needs to be refactored to just return dict return {inst_type['name']: dict(memory_mb=inst_type['memory_mb'], vcpus=inst_type['vcpus'], local_gb=inst_type['local_gb'], @@ -2078,6 +2079,7 @@ def instance_type_get_by_flavor_id(context, id): inst_type = session.query(models.InstanceTypes).\ filter_by(flavorid=int(id)).\ first() + # FIXME(kpepple) this needs to be refactored to just return dict return {inst_type['name']: dict(memory_mb=inst_type['memory_mb'], vcpus=inst_type['vcpus'], local_gb=inst_type['local_gb'], -- cgit From ea5271ed69d72dcab8189c3bfc66220c7ff60862 Mon Sep 17 00:00:00 2001 From: Ken Pepple Date: Sun, 6 Feb 2011 10:56:05 -0800 Subject: refactor to remove ugly code in flavors --- nova/api/openstack/flavors.py | 9 ++++----- nova/tests/db/fakes.py | 2 ++ 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/nova/api/openstack/flavors.py b/nova/api/openstack/flavors.py index da38dd34d..3124c26b2 100644 --- a/nova/api/openstack/flavors.py +++ b/nova/api/openstack/flavors.py @@ -47,9 +47,10 @@ class Controller(wsgi.Controller): def show(self, req, id): """Return data about the given flavor id.""" - # FIXME(kpepple) do we need admin context here ? + # FIXME(kpepple) do we really need admin context here ? ctxt = context.get_admin_context() val = db.instance_type_get_by_flavor_id(ctxt, id) + # FIXME(kpepple) refactor db call to return dict v = val.values()[0] item = dict(ram=v['memory_mb'], disk=v['local_gb'], id=v['flavorid'], name=val.keys()[0]) @@ -58,10 +59,8 @@ class Controller(wsgi.Controller): def _all_ids(self): """Return the list of all flavorids.""" - # FIXME(kpepple) do we need admin context here ? + # FIXME(kpepple) do we really need admin context here ? ctxt = context.get_admin_context() - flavor_ids = [] inst_types = db.instance_type_get_all(ctxt) - for i in inst_types.keys(): - flavor_ids.append(inst_types[i]['flavorid']) + flavor_ids = [inst_types[i]['flavorid'] for i in inst_types.keys()] return flavor_ids diff --git a/nova/tests/db/fakes.py b/nova/tests/db/fakes.py index 3b47fc867..859ed6edc 100644 --- a/nova/tests/db/fakes.py +++ b/nova/tests/db/fakes.py @@ -46,6 +46,8 @@ def stub_out_db_instance_api(stubs): # FIXME(kpepple) for dynamic flavors type_data = instance_types.INSTANCE_TYPES[values['instance_type']] + # type_data = db.instance_type_get_by_name(context,\ + # instance_type)[instance_type] base_options = { 'name': values['name'], -- cgit From 7dcdbcc546248c3384bd15975a721413e1d1f507 Mon Sep 17 00:00:00 2001 From: Ken Pepple Date: Sun, 6 Feb 2011 13:28:07 -0800 Subject: simplified instance_types db calls to return entire row - we may need these extra columns for some features and there seems to be little downside in including them. still need to fix testing calls. --- bin/nova-manage | 10 +++++--- nova/api/ec2/admin.py | 5 ++-- nova/api/openstack/flavors.py | 10 ++++---- nova/compute/api.py | 2 +- nova/db/sqlalchemy/api.py | 41 +++++++++----------------------- nova/tests/api/openstack/test_flavors.py | 22 ----------------- nova/tests/db/fakes.py | 2 +- 7 files changed, 28 insertions(+), 64 deletions(-) diff --git a/bin/nova-manage b/bin/nova-manage index 8e2c7962f..73d69fc64 100755 --- a/bin/nova-manage +++ b/bin/nova-manage @@ -649,12 +649,16 @@ class InstanceTypesCommands(object): instance_types = db.instance_type_get_all(ctxt) if len(instance_types) < 1: sys.exit(1) + for k, v in instance_types.iteritems(): + print "%s : %s memory(MB), %s vcpus, %s storage(GB)" % \ + (k, v["memory_mb"], + v["vcpus"], v["local_gb"]) else: instance_types = db.instance_type_get_by_name(ctxt, name) - for k, v in instance_types.iteritems(): print "%s : %s memory(MB), %s vcpus, %s storage(GB)" % \ - (k, v["memory_mb"], - v["vcpus"], v["local_gb"]) + (instance_types["name"], instance_types["memory_mb"], + instance_types["vcpus"], instance_types["local_gb"]) + return diff --git a/nova/api/ec2/admin.py b/nova/api/ec2/admin.py index 5a9e22bf7..4c0628e21 100644 --- a/nova/api/ec2/admin.py +++ b/nova/api/ec2/admin.py @@ -84,8 +84,9 @@ class AdminController(object): """Returns all active instance types data (vcpus, memory, etc.)""" # return {'instanceTypeSet': [instance_dict(n, v) for n, v in # instance_types.INSTANCE_TYPES.iteritems()]} - return {'instanceTypeSet': - [for i in db.instance_type_get_all(): instance_dict(i)]} + # return {'instanceTypeSet': + # [for i in db.instance_type_get_all(): instance_dict(i)]} + return {'instanceTypeSet': [db.instance_type_get_all(_context)]} # FIXME(kpepple) this is untested code path. def describe_instance_type(self, _context, name, **_kwargs): diff --git a/nova/api/openstack/flavors.py b/nova/api/openstack/flavors.py index 3124c26b2..9b674afbd 100644 --- a/nova/api/openstack/flavors.py +++ b/nova/api/openstack/flavors.py @@ -49,12 +49,12 @@ class Controller(wsgi.Controller): """Return data about the given flavor id.""" # FIXME(kpepple) do we really need admin context here ? ctxt = context.get_admin_context() - val = db.instance_type_get_by_flavor_id(ctxt, id) + values = db.instance_type_get_by_flavor_id(ctxt, id) # FIXME(kpepple) refactor db call to return dict - v = val.values()[0] - item = dict(ram=v['memory_mb'], disk=v['local_gb'], - id=v['flavorid'], name=val.keys()[0]) - return dict(flavor=item) + # v = val.values()[0] + # item = dict(ram=v['memory_mb'], disk=v['local_gb'], + # id=v['flavorid'], name=val.keys()[0]) + return dict(flavor=values) raise faults.Fault(exc.HTTPNotFound()) def _all_ids(self): diff --git a/nova/compute/api.py b/nova/compute/api.py index 006db880e..fc18765f6 100644 --- a/nova/compute/api.py +++ b/nova/compute/api.py @@ -91,7 +91,7 @@ class API(base.Base): # FIXME(kpepple) this needs to be factored for api.py:2065 refactor type_data = db.instance_type_get_by_name(context,\ - instance_type)[instance_type] + instance_type) num_instances = quota.allowed_instances(context, max_count, type_data) if num_instances < min_count: pid = context.project_id diff --git a/nova/db/sqlalchemy/api.py b/nova/db/sqlalchemy/api.py index eff03aa02..f4e021ac8 100644 --- a/nova/db/sqlalchemy/api.py +++ b/nova/db/sqlalchemy/api.py @@ -2032,10 +2032,11 @@ def instance_type_create(context, values): def instance_type_get_all(context): - """Returns a dict of all instance_types: - { 'm1.tiny': dict(memory_mb=512, vcpus=1, local_gb=0, flavorid=1), - 'm1.small': dict(memory_mb=2048, vcpus=1, local_gb=20, flavorid=2), - 'm1.medium': dict(memory_mb=4096, vcpus=2, local_gb=40, flavorid=3)} + """ + Returns a dict describing all instance_types with name as key: + {'m1.tiny': dict(memory_mb=512, vcpus=1, local_gb=0, flavorid=1), + 'm1.small': dict(memory_mb=2048, vcpus=1, local_gb=20, flavorid=2), + 'm1.medium': dict(memory_mb=4096, vcpus=2, local_gb=40, flavorid=3)} """ session = get_session() inst_types = session.query(models.InstanceTypes).\ @@ -2044,51 +2045,31 @@ def instance_type_get_all(context): all() inst_dict = {} for i in inst_types: - inst_dict[i['name']] = dict(memory_mb=i['memory_mb'], - vcpus=i['vcpus'], - local_gb=i['local_gb'], - flavorid=i['flavorid'], - deleted=i['deleted']) - + inst_dict[i['name']] = dict(i) return inst_dict def instance_type_get_by_name(context, name): - """ - Returns a dict of specific instance_type: - {'m1.tiny': dict(memory_mb=512, vcpus=1, local_gb=0, flavorid=1)} - """ + """Returns a dict describing specific instance_type""" session = get_session() inst_type = session.query(models.InstanceTypes).\ filter_by(name=name).\ first() - # FIXME(kpepple) this needs to be refactored to just return dict - return {inst_type['name']: dict(memory_mb=inst_type['memory_mb'], - vcpus=inst_type['vcpus'], - local_gb=inst_type['local_gb'], - flavorid=inst_type['flavorid'], - deleted=inst_type['deleted'])} + return dict(inst_type) def instance_type_get_by_flavor_id(context, id): - """ - Returns a dict of specific flavor_id: - {'m1.tiny': dict(memory_mb=512, vcpus=1, local_gb=0, flavorid=1)} - """ + """Returns a dict describing specific flavor_id""" session = get_session() inst_type = session.query(models.InstanceTypes).\ filter_by(flavorid=int(id)).\ first() - # FIXME(kpepple) this needs to be refactored to just return dict - return {inst_type['name']: dict(memory_mb=inst_type['memory_mb'], - vcpus=inst_type['vcpus'], - local_gb=inst_type['local_gb'], - flavorid=inst_type['flavorid'], - deleted=inst_type['deleted'])} + return dict(inst_type) @require_admin_context def instance_type_destroy(context, name): + """ Marks specific instance_type as deleted""" session = get_session() instance_type_ref = session.query(models.InstanceTypes).\ filter_by(name=name) diff --git a/nova/tests/api/openstack/test_flavors.py b/nova/tests/api/openstack/test_flavors.py index 9287e8adf..532b34e1b 100644 --- a/nova/tests/api/openstack/test_flavors.py +++ b/nova/tests/api/openstack/test_flavors.py @@ -44,28 +44,6 @@ class FlavorsTest(unittest.TestCase): req = webob.Request.blank('/v1.0/flavors') res = req.get_response(fakes.wsgi_app()) - def test_create_list_delete_favor(self): - # create a new flavor - starting_flavors = db.instance_type_get_all(self.context) - new_instance_type = dict(name="os1.big", memory_mb=512, - vcpus=1, local_gb=120, flavorid=25) - new_flavor = db.instance_type_create(self.context, new_instance_type) - self.assertEqual(new_flavor["name"], new_instance_type["name"]) - # retrieve the newly created flavor - retrieved_new_flavor = db.instance_type_get_by_name( - self.context, - new_instance_type["name"]) - self.assertEqual(retrieved_new_flavor.values()[0]["memory_mb"], - new_instance_type["memory_mb"]) - flavors = db.instance_type_get_all(self.context) - self.assertNotEqual(starting_flavors, flavors) - # delete the newly created flavor - delete_query = db.instance_type_destroy(self.context, - new_instance_type["name"]) - deleted_flavor = db.instance_type_get_by_name(self.context, - new_instance_type["name"]) - self.assertEqual(deleted_flavor.values()[0]["deleted"], 1) - if __name__ == '__main__': unittest.main() diff --git a/nova/tests/db/fakes.py b/nova/tests/db/fakes.py index 859ed6edc..3aa7fcd22 100644 --- a/nova/tests/db/fakes.py +++ b/nova/tests/db/fakes.py @@ -47,7 +47,7 @@ def stub_out_db_instance_api(stubs): # FIXME(kpepple) for dynamic flavors type_data = instance_types.INSTANCE_TYPES[values['instance_type']] # type_data = db.instance_type_get_by_name(context,\ - # instance_type)[instance_type] + # instance_type) base_options = { 'name': values['name'], -- cgit From ea5ba79802321bb25f03dfb24fd7fb01866d9921 Mon Sep 17 00:00:00 2001 From: Ken Pepple Date: Sun, 6 Feb 2011 13:48:03 -0800 Subject: aliased flavor to instance_types in nova-manage. will probably need to make flavor a full fledged class as users will want to list flavors by flavor name --- bin/nova-manage | 5 +++-- nova/tests/test_nova_manage.py | 9 +++++---- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/bin/nova-manage b/bin/nova-manage index 73d69fc64..c3fa9cf3f 100755 --- a/bin/nova-manage +++ b/bin/nova-manage @@ -609,7 +609,7 @@ class VolumeCommands(object): "mountpoint": volume['mountpoint']}}) -class InstanceTypesCommands(object): +class InstanceTypeCommands(object): """Class for managing instance types / flavors.""" def create(self, name, memory, vcpus, local_gb, flavorid): @@ -674,7 +674,8 @@ CATEGORIES = [ ('log', LogCommands), ('db', DbCommands), ('volume', VolumeCommands), - ('instance_types', InstanceTypesCommands)] + ('instance_type', InstanceTypeCommands), + ('flavor', InstanceTypeCommands)] def lazy_match(name, key_value_tuples): diff --git a/nova/tests/test_nova_manage.py b/nova/tests/test_nova_manage.py index 09ed8163b..487f172ff 100644 --- a/nova/tests/test_nova_manage.py +++ b/nova/tests/test_nova_manage.py @@ -39,11 +39,12 @@ class NovaManageTestCase(test.TestCase): "delete", "test"], stdout=fnull) self.assertEqual(0, retcode) - def test_list_instance_types(self): + def test_list_instance_types_or_flavors(self): fnull = open(os.devnull, 'w') - retcode = subprocess.call(["bin/nova-manage", "instance_type", \ - "list"], stdout=fnull) - self.assertEqual(0, retcode) + for c in ["instance_type", "flavor"]: + retcode = subprocess.call(["bin/nova-manage", c, \ + "list"], stdout=fnull) + self.assertEqual(0, retcode) def test_list_specific_instance_type(self): fnull = open(os.devnull, 'w') -- cgit From 2458d674807d951a6b58c28cd334cd8d097822a9 Mon Sep 17 00:00:00 2001 From: Cerberus Date: Mon, 7 Feb 2011 10:20:49 -0600 Subject: A few changes --- nova/compute/manager.py | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/nova/compute/manager.py b/nova/compute/manager.py index 140db0d3e..485efc047 100644 --- a/nova/compute/manager.py +++ b/nova/compute/manager.py @@ -380,6 +380,20 @@ class ComputeManager(manager.Manager): """Update instance state when async task completes.""" self._update_state(context, instance_id) + @exception.wrap_exception + @checks_instance_lock + def confirm_resize(self, context, instance_id): + """Destroys the old instance on the source machine""" + pass + + @exception.wrap_exception + @echecks_instance_lock + def revert_resize(self, context, instance_id): + """Destroys the new instance on the destination machine, + reverts the model changes, and powers on the old + instance on the source machine""" + pass + @exception.wrap_exception @checks_instance_lock @@ -395,8 +409,9 @@ class ComputeManager(manager.Manager): 'status': 'pre-migrating' } LOG.audit(_('instance %s: migrating to '), instance_id, context=context) service = self.db.service_get_by_host_and_topic(context, - instance_ref['host'], topic) - topic = self.db.queue_get_for(context, topic, service['id']) + instance_ref['host'], FLAGS.compute_topic) + topic = self.db.queue_get_for(context, FLAGS.compute_topic, + service['id']) rpc.cast(context, topic, { 'method': 'resize_instance', 'migration_id': migration_ref['id'], } @@ -417,8 +432,9 @@ class ComputeManager(manager.Manager): # This is where we would update the VM record after resizing service = self.db.service_get_by_host_and_topic(context, - migration_ref['dest_host'], topic) - topic = self.db.queue_get_for(context, topic, service['id']) + migration_ref['dest_host'], FLAGS.compute_topic) + topic = self.db.queue_get_for(context, FLAGS.compute_topic, + service['id']) rpc.cast(context, topic, { 'method': 'finish_resize', 'migration_id': migration_ref['id'], } -- cgit From 5a5e96ae90187e1ebfe93262df47ec2c4be23ef1 Mon Sep 17 00:00:00 2001 From: Ken Pepple Date: Mon, 7 Feb 2011 13:00:39 -0800 Subject: require user context for most flavor/instance_type read calls --- nova/db/sqlalchemy/api.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/nova/db/sqlalchemy/api.py b/nova/db/sqlalchemy/api.py index f4e021ac8..6065af9ca 100644 --- a/nova/db/sqlalchemy/api.py +++ b/nova/db/sqlalchemy/api.py @@ -2031,6 +2031,7 @@ def instance_type_create(context, values): return instance_type_ref +@require_context def instance_type_get_all(context): """ Returns a dict describing all instance_types with name as key: @@ -2049,6 +2050,7 @@ def instance_type_get_all(context): return inst_dict +@require_context def instance_type_get_by_name(context, name): """Returns a dict describing specific instance_type""" session = get_session() @@ -2058,6 +2060,7 @@ def instance_type_get_by_name(context, name): return dict(inst_type) +@require_context def instance_type_get_by_flavor_id(context, id): """Returns a dict describing specific flavor_id""" session = get_session() -- cgit From 346087804dd923bcaa0faf433dc1f83a2f193815 Mon Sep 17 00:00:00 2001 From: Ken Pepple Date: Mon, 7 Feb 2011 13:32:25 -0800 Subject: fixed instance_types methods to use database backend --- nova/compute/instance_types.py | 37 +++++++++++++++++++++++++++++-------- 1 file changed, 29 insertions(+), 8 deletions(-) diff --git a/nova/compute/instance_types.py b/nova/compute/instance_types.py index 449ec1d2e..d38d76ded 100644 --- a/nova/compute/instance_types.py +++ b/nova/compute/instance_types.py @@ -21,6 +21,8 @@ The built-in instance properties. """ +from nova import context +from nova import db from nova import flags from nova import exception @@ -34,20 +36,39 @@ INSTANCE_TYPES = { 'm1.xlarge': dict(memory_mb=16384, vcpus=8, local_gb=160, flavorid=5)} +def get_all_types(): + """retrieves all instance_types""" + return db.instance_type_get_all() + + +def get_all_flavors(): + """retrieves all flavors. alias for instance_types.get_all_types()""" + return get_all_types() + + def get_by_type(instance_type): - """Build instance data structure and save it to the data store.""" + """retrieve instance_type details""" # FIXME(kpepple) for dynamic flavors if instance_type is None: return FLAGS.default_instance_type - if instance_type not in INSTANCE_TYPES: + try: + ctxt = context.get_admin_context() + inst_type = db.instance_type_get_by_name(ctxt, instance_type) + except Exception, e: + print e raise exception.ApiError(_("Unknown instance type: %s"), instance_type) - return instance_type + return inst_type['name'] def get_by_flavor_id(flavor_id): - # FIXME(kpepple) for dynamic flavors - for instance_type, details in INSTANCE_TYPES.iteritems(): - if details['flavorid'] == flavor_id: - return instance_type - return FLAGS.default_instance_type + """retrieve instance_type's name by flavor_id""" + if flavor_id is None: + return FLAGS.default_instance_type + try: + ctxt = context.get_admin_context() + flavor = db.instance_type_get_by_flavor_id(ctxt, flavor_id) + except Exception, e: + raise exception.ApiError(_("Unknown flavor: %s"), + flavor_id) + return flavor['name'] -- cgit From e59c62efe5492e59fcc26b7b74f6ac2daa0caabe Mon Sep 17 00:00:00 2001 From: Cerberus Date: Mon, 7 Feb 2011 16:57:02 -0600 Subject: Added data_transfer xapi plugin --- nova/virt/xenapi_conn.py | 2 +- .../xenapi/etc/xapi.d/plugins/data_transfer | 44 ++++++++++++++++++++++ 2 files changed, 45 insertions(+), 1 deletion(-) create mode 100644 plugins/xenserver/xenapi/etc/xapi.d/plugins/data_transfer diff --git a/nova/virt/xenapi_conn.py b/nova/virt/xenapi_conn.py index 628291764..acfde6caf 100644 --- a/nova/virt/xenapi_conn.py +++ b/nova/virt/xenapi_conn.py @@ -193,7 +193,7 @@ class XenAPIConnection(object): self._vmops.power_on(instance) def transfer_disk(self, instance, dest, callback): - self._vmops.transfer_disk( + self._vmops.transfer_disk() def suspend(self, instance, callback): """suspend the specified instance""" diff --git a/plugins/xenserver/xenapi/etc/xapi.d/plugins/data_transfer b/plugins/xenserver/xenapi/etc/xapi.d/plugins/data_transfer new file mode 100644 index 000000000..d310a65d9 --- /dev/null +++ b/plugins/xenserver/xenapi/etc/xapi.d/plugins/data_transfer @@ -0,0 +1,44 @@ +#!/usr/bin/env python + +# 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. + +""" +XenAPI Plugin for transfering data between host nodes +""" + +import os.path +import subprocess + +import XenAPIPlugin + +SSH_HOSTS = '/root/.ssh/known_hosts' +DEVNULL = '/dev/null' +KEYSCAN = '/usr/bin/ssh-keyscan' + +def _key_scan_and_add(host): + """SSH scans a remote host and writes the SSH key out to known_hosts""" + open(SSH_HOSTS, 'a').close() + null = open(DEVNULL, 'w') + known_hosts = open(SSH_HOSTS, 'a') + key = subprocess.Popen(['/usr/bin/ssh-keyscan', '-t', 'rsa', host], + stdout=subprocess.PIPE, stderr=null).communicate()[0].strip() + grep = subprocess.call(['/bin/grep', '-o', '%s' % key, SSH_HOSTS], + stdout=null, stderr=null) + if grep == 1: + known_hosts.write(key) + null.close() + known_hosts.close() -- cgit From bb2a379e2827ceccc5b46b0a9936e6dcedc6499e Mon Sep 17 00:00:00 2001 From: Ken Pepple Date: Mon, 7 Feb 2011 15:04:26 -0800 Subject: added INSTANCE_TYPES to test for compatibility with current tests --- nova/compute/instance_types.py | 12 ++++++------ nova/test.py | 8 ++++++++ nova/tests/db/fakes.py | 6 ++---- nova/tests/test_quota.py | 9 +++------ nova/tests/test_xenapi.py | 3 +-- nova/virt/libvirt_conn.py | 7 +++---- nova/virt/xenapi/vm_utils.py | 4 ++-- 7 files changed, 25 insertions(+), 24 deletions(-) diff --git a/nova/compute/instance_types.py b/nova/compute/instance_types.py index d38d76ded..0e20982e3 100644 --- a/nova/compute/instance_types.py +++ b/nova/compute/instance_types.py @@ -28,12 +28,12 @@ from nova import exception FLAGS = flags.FLAGS # FIXME(kpepple) for dynamic flavors -INSTANCE_TYPES = { - 'm1.tiny': dict(memory_mb=512, vcpus=1, local_gb=0, flavorid=1), - 'm1.small': dict(memory_mb=2048, vcpus=1, local_gb=20, flavorid=2), - 'm1.medium': dict(memory_mb=4096, vcpus=2, local_gb=40, flavorid=3), - 'm1.large': dict(memory_mb=8192, vcpus=4, local_gb=80, flavorid=4), - 'm1.xlarge': dict(memory_mb=16384, vcpus=8, local_gb=160, flavorid=5)} +# INSTANCE_TYPES = { +# 'm1.tiny': dict(memory_mb=512, vcpus=1, local_gb=0, flavorid=1), +# 'm1.small': dict(memory_mb=2048, vcpus=1, local_gb=20, flavorid=2), +# 'm1.medium': dict(memory_mb=4096, vcpus=2, local_gb=40, flavorid=3), +# 'm1.large': dict(memory_mb=8192, vcpus=4, local_gb=80, flavorid=4), +# 'm1.xlarge': dict(memory_mb=16384, vcpus=8, local_gb=160, flavorid=5)} def get_all_types(): diff --git a/nova/test.py b/nova/test.py index 881baccd5..cd049f007 100644 --- a/nova/test.py +++ b/nova/test.py @@ -44,6 +44,14 @@ flags.DEFINE_bool('fake_tests', True, 'should we use everything for testing') +INSTANCE_TYPES = { + 'm1.tiny': dict(memory_mb=512, vcpus=1, local_gb=0, flavorid=1), + 'm1.small': dict(memory_mb=2048, vcpus=1, local_gb=20, flavorid=2), + 'm1.medium': dict(memory_mb=4096, vcpus=2, local_gb=40, flavorid=3), + 'm1.large': dict(memory_mb=8192, vcpus=4, local_gb=80, flavorid=4), + 'm1.xlarge': dict(memory_mb=16384, vcpus=8, local_gb=160, flavorid=5)} + + def skip_if_fake(func): """Decorator that skips a test if running in fake mode""" def _skipper(*args, **kw): diff --git a/nova/tests/db/fakes.py b/nova/tests/db/fakes.py index 3aa7fcd22..d9a5032ee 100644 --- a/nova/tests/db/fakes.py +++ b/nova/tests/db/fakes.py @@ -20,6 +20,7 @@ import time from nova import db +from nova import test from nova import utils from nova.compute import instance_types @@ -44,10 +45,7 @@ def stub_out_db_instance_api(stubs): def fake_instance_create(values): """ Stubs out the db.instance_create method """ - # FIXME(kpepple) for dynamic flavors - type_data = instance_types.INSTANCE_TYPES[values['instance_type']] - # type_data = db.instance_type_get_by_name(context,\ - # instance_type) + type_data = test.INSTANCE_TYPES[values['instance_type']] base_options = { 'name': values['name'], diff --git a/nova/tests/test_quota.py b/nova/tests/test_quota.py index c70803e05..4206ca416 100644 --- a/nova/tests/test_quota.py +++ b/nova/tests/test_quota.py @@ -75,20 +75,17 @@ class QuotaTestCase(test.TestCase): def test_quota_overrides(self): """Make sure overriding a projects quotas works""" - # FIXME(kpepple) for dynamic flavors num_instances = quota.allowed_instances(self.context, 100, - instance_types.INSTANCE_TYPES['m1.small']) + test.INSTANCE_TYPES['m1.small']) self.assertEqual(num_instances, 2) db.quota_create(self.context, {'project_id': self.project.id, 'instances': 10}) - # FIXME(kpepple) for dynamic flavors num_instances = quota.allowed_instances(self.context, 100, - instance_types.INSTANCE_TYPES['m1.small']) + test.INSTANCE_TYPES['m1.small']) self.assertEqual(num_instances, 4) db.quota_update(self.context, self.project.id, {'cores': 100}) - # FIXME(kpepple) for dynamic flavors num_instances = quota.allowed_instances(self.context, 100, - instance_types.INSTANCE_TYPES['m1.small']) + test.INSTANCE_TYPES['m1.small']) self.assertEqual(num_instances, 10) db.quota_destroy(self.context, self.project.id) diff --git a/nova/tests/test_xenapi.py b/nova/tests/test_xenapi.py index 1d42f8da3..d8611ea10 100644 --- a/nova/tests/test_xenapi.py +++ b/nova/tests/test_xenapi.py @@ -225,8 +225,7 @@ class XenAPIVMTestCase(test.TestCase): vm = vms[0] # Check that m1.large above turned into the right thing. - # FIXME(kpepple) for dynamic flavors - instance_type = instance_types.INSTANCE_TYPES['m1.large'] + instance_type = test.INSTANCE_TYPES['m1.large'] mem_kib = long(instance_type['memory_mb']) << 10 mem_bytes = str(mem_kib << 10) vcpus = instance_type['vcpus'] diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py index e66115464..94fe93c40 100644 --- a/nova/virt/libvirt_conn.py +++ b/nova/virt/libvirt_conn.py @@ -55,6 +55,7 @@ from nova import db from nova import exception from nova import flags from nova import log as logging +from nova import test from nova import utils #from nova.api import context from nova.auth import manager @@ -611,8 +612,7 @@ class LibvirtConnection(object): user=user, project=project, size=size) - # FIXME(kpepple) for dynamic flavors - type_data = instance_types.INSTANCE_TYPES[inst['instance_type']] + type_data = test.INSTANCE_TYPES[inst['instance_type']] if type_data['local_gb']: self._cache_image(fn=self._create_local, @@ -672,9 +672,8 @@ class LibvirtConnection(object): network = db.network_get_by_instance(context.get_admin_context(), instance['id']) # FIXME(vish): stick this in db - # FIXME(kpepple) for dynamic flavors instance_type = instance['instance_type'] - instance_type = instance_types.INSTANCE_TYPES[instance_type] + instance_type = test.INSTANCE_TYPES[instance_type] ip_address = db.instance_get_fixed_address(context.get_admin_context(), instance['id']) # Assume that the gateway also acts as the dhcp server. diff --git a/nova/virt/xenapi/vm_utils.py b/nova/virt/xenapi/vm_utils.py index 3f0112609..38a6f73ce 100644 --- a/nova/virt/xenapi/vm_utils.py +++ b/nova/virt/xenapi/vm_utils.py @@ -31,6 +31,7 @@ import glance.client from nova import exception from nova import flags from nova import log as logging +from nova import test from nova import utils from nova.auth.manager import AuthManager from nova.compute import instance_types @@ -82,8 +83,7 @@ class VMHelper(HelperBase): the pv_kernel flag indicates whether the guest is HVM or PV """ - # FIXME(kpepple) for dynamic flavors - instance_type = instance_types.INSTANCE_TYPES[instance.instance_type] + instance_type = test.INSTANCE_TYPES[instance.instance_type] mem = str(long(instance_type['memory_mb']) * 1024 * 1024) vcpus = str(instance_type['vcpus']) rec = { -- cgit From a40f6041556ec09a1cb79c2b8abcec7fa70e72bf Mon Sep 17 00:00:00 2001 From: Cerberus Date: Mon, 7 Feb 2011 17:12:15 -0600 Subject: Some stuff --- nova/virt/xenapi_conn.py | 2 +- plugins/xenserver/xenapi/etc/xapi.d/plugins/data_transfer | 12 ++++++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/nova/virt/xenapi_conn.py b/nova/virt/xenapi_conn.py index acfde6caf..726106b37 100644 --- a/nova/virt/xenapi_conn.py +++ b/nova/virt/xenapi_conn.py @@ -193,7 +193,7 @@ class XenAPIConnection(object): self._vmops.power_on(instance) def transfer_disk(self, instance, dest, callback): - self._vmops.transfer_disk() + self._vmops.transfer_disk(dest) def suspend(self, instance, callback): """suspend the specified instance""" diff --git a/plugins/xenserver/xenapi/etc/xapi.d/plugins/data_transfer b/plugins/xenserver/xenapi/etc/xapi.d/plugins/data_transfer index d310a65d9..cde7bb823 100644 --- a/plugins/xenserver/xenapi/etc/xapi.d/plugins/data_transfer +++ b/plugins/xenserver/xenapi/etc/xapi.d/plugins/data_transfer @@ -28,10 +28,13 @@ import XenAPIPlugin SSH_HOSTS = '/root/.ssh/known_hosts' DEVNULL = '/dev/null' KEYSCAN = '/usr/bin/ssh-keyscan' +RSYNC = '/usr/bin/rsync' def _key_scan_and_add(host): """SSH scans a remote host and writes the SSH key out to known_hosts""" + # Touch the file if it doesn't yet exist open(SSH_HOSTS, 'a').close() + null = open(DEVNULL, 'w') known_hosts = open(SSH_HOSTS, 'a') key = subprocess.Popen(['/usr/bin/ssh-keyscan', '-t', 'rsa', host], @@ -42,3 +45,12 @@ def _key_scan_and_add(host): known_hosts.write(key) null.close() known_hosts.close() + +def transfer_vhd(host, vhd_path): + """Rsyncs a VHD to an adjacent host""" + _key_scan_and_add(host) + if subprocess.call([RSYNC, vhd_path, "%s:/root/" % host]) != 0: + raise Exception("Unexpected VHD transfer failure") + +if __name__ == '__main__': + XenAPIPlugin.dispatch({'transfer_vhd': transfer_vhd}) -- cgit From 203c94c89caabc1d4ece4c462819a90c05cde163 Mon Sep 17 00:00:00 2001 From: Cerberus Date: Mon, 7 Feb 2011 17:39:53 -0600 Subject: blargh --- nova/compute/api.py | 2 +- nova/virt/xenapi/vmops.py | 8 ++++++++ nova/virt/xenapi_conn.py | 2 +- plugins/xenserver/xenapi/etc/xapi.d/plugins/data_transfer | 6 +++--- 4 files changed, 13 insertions(+), 5 deletions(-) diff --git a/nova/compute/api.py b/nova/compute/api.py index f0d5ff2cb..6b2628378 100644 --- a/nova/compute/api.py +++ b/nova/compute/api.py @@ -379,7 +379,7 @@ class API(base.Base): kwargs = {'method': method, 'args': params} return rpc.call(context, queue, kwargs) - def _cast_scheduler_message(self, context, args) + def _cast_scheduler_message(self, context, args): """Generic handler for RPC calls to the scheduler""" rpc.cast(context, FLAGS.scheduler_topic, args) diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py index 468881355..4b835c707 100644 --- a/nova/virt/xenapi/vmops.py +++ b/nova/virt/xenapi/vmops.py @@ -220,6 +220,14 @@ class VMOps(object): logging.debug(_("Finished snapshot and upload for VM %s"), instance) + def transfer_disk(self, instance, dest): + """ Copies a VHD from one host machine to another + + :param instance: the instance that owns the VHD in question + :param dest: the destination host machine + """ + + def resize(self, instance, flavor): """Resize a running instance by changing it's RAM and disk size """ raise NotImplementedError() diff --git a/nova/virt/xenapi_conn.py b/nova/virt/xenapi_conn.py index 726106b37..2e587117a 100644 --- a/nova/virt/xenapi_conn.py +++ b/nova/virt/xenapi_conn.py @@ -193,7 +193,7 @@ class XenAPIConnection(object): self._vmops.power_on(instance) def transfer_disk(self, instance, dest, callback): - self._vmops.transfer_disk(dest) + self._vmops.transfer_disk(instance, dest) def suspend(self, instance, callback): """suspend the specified instance""" diff --git a/plugins/xenserver/xenapi/etc/xapi.d/plugins/data_transfer b/plugins/xenserver/xenapi/etc/xapi.d/plugins/data_transfer index cde7bb823..2af4a758b 100644 --- a/plugins/xenserver/xenapi/etc/xapi.d/plugins/data_transfer +++ b/plugins/xenserver/xenapi/etc/xapi.d/plugins/data_transfer @@ -46,11 +46,11 @@ def _key_scan_and_add(host): null.close() known_hosts.close() -def transfer_vhd(host, vhd_path): +def transfer_file(host, file_path): """Rsyncs a VHD to an adjacent host""" _key_scan_and_add(host) - if subprocess.call([RSYNC, vhd_path, "%s:/root/" % host]) != 0: + if subprocess.call([RSYNC, file_path, "%s:/root/" % host]) != 0: raise Exception("Unexpected VHD transfer failure") if __name__ == '__main__': - XenAPIPlugin.dispatch({'transfer_vhd': transfer_vhd}) + XenAPIPlugin.dispatch({'transfer_file': transfer_file}) -- cgit From 3f2cd17011e17991ebf1a77605686ce3dc48d92e Mon Sep 17 00:00:00 2001 From: Cerberus Date: Tue, 8 Feb 2011 10:47:23 -0600 Subject: Changes and bug fixes --- nova/compute/manager.py | 8 ++-- nova/db/sqlalchemy/api.py | 6 +-- .../sqlalchemy/migrate_repo/versions/003_cactus.py | 46 ++++++++++++++++++++++ nova/db/sqlalchemy/migration.py | 2 +- nova/virt/xenapi/vmops.py | 2 +- 5 files changed, 54 insertions(+), 10 deletions(-) create mode 100644 nova/db/sqlalchemy/migrate_repo/versions/003_cactus.py diff --git a/nova/compute/manager.py b/nova/compute/manager.py index 485efc047..4189c49a4 100644 --- a/nova/compute/manager.py +++ b/nova/compute/manager.py @@ -387,7 +387,7 @@ class ComputeManager(manager.Manager): pass @exception.wrap_exception - @echecks_instance_lock + @checks_instance_lock def revert_resize(self, context, instance_id): """Destroys the new instance on the destination machine, reverts the model changes, and powers on the old @@ -406,7 +406,7 @@ class ComputeManager(manager.Manager): { 'instance_id': instance_id, 'source_host': instance_ref['host'], 'dest_host': socket.gethostbyname(socket.gethostname()), - 'status': 'pre-migrating' } + 'status': 'pre-migrating' }) LOG.audit(_('instance %s: migrating to '), instance_id, context=context) service = self.db.service_get_by_host_and_topic(context, instance_ref['host'], FLAGS.compute_topic) @@ -414,7 +414,7 @@ class ComputeManager(manager.Manager): service['id']) rpc.cast(context, topic, { 'method': 'resize_instance', - 'migration_id': migration_ref['id'], } + 'migration_id': migration_ref['id'], }) @exception.wrap_exception @checks_instance_lock @@ -437,7 +437,7 @@ class ComputeManager(manager.Manager): service['id']) rpc.cast(context, topic, { 'method': 'finish_resize', - 'migration_id': migration_ref['id'], } + 'migration_id': migration_ref['id'], }) @exception.wrap_exception @checks_instance_lock diff --git a/nova/db/sqlalchemy/api.py b/nova/db/sqlalchemy/api.py index e94f9f4d2..ece1cd373 100644 --- a/nova/db/sqlalchemy/api.py +++ b/nova/db/sqlalchemy/api.py @@ -1942,8 +1942,7 @@ def migration_update(context, migration_id, values): def migration_get(context, migration_id): session = get_session() result = session.query(models.Migration.\ - filter_by(migration_id=migration_id)). - first() + filter_by(migration_id=migration_id)).first() if not result: raise exception.NotFound(_("No migration found with id %s") % migration_id) @@ -1954,8 +1953,7 @@ def migration_get(context, migration_id): def migration_get_by_instance(context, instance_id): session = get_session() result = session.query(models.Migration.\ - filter_by(instance_id=instance_id)). - first() + filter_by(instance_id=instance_id)).first() if not result: raise exception.NotFound(_("No migration found with instance id %s") % migration_id) diff --git a/nova/db/sqlalchemy/migrate_repo/versions/003_cactus.py b/nova/db/sqlalchemy/migrate_repo/versions/003_cactus.py new file mode 100644 index 000000000..bbe5cbcb0 --- /dev/null +++ b/nova/db/sqlalchemy/migrate_repo/versions/003_cactus.py @@ -0,0 +1,46 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright 2010 OpenStack LLC. +# 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.from sqlalchemy import * + +from migrate import * + +from nova import log as logging + + +meta = MetaData() + +# +# New Tables +# + +migrations = Table('migrations', meta, + Column('source_host', String(255)) + Column('dest_host', String(255)) + Column('instance_id', Integer, ForeignKey('instances.id'), nullable=True) + Column('status', String(255)) + ) + +def upgrade(migrate_engine): + # Upgrade operations go here. Don't create your own engine; + # bind migrate_engine to your metadata + meta.bind = migrate_engine + for table in (migrations): + try: + table.create() + except Exception: + logging.info(repr(table)) + logging.exception('Exception while creating table') + raise diff --git a/nova/db/sqlalchemy/migration.py b/nova/db/sqlalchemy/migration.py index 2a13c5466..644e3e45e 100644 --- a/nova/db/sqlalchemy/migration.py +++ b/nova/db/sqlalchemy/migration.py @@ -50,7 +50,7 @@ def db_version(): 'key_pairs', 'networks', 'projects', 'quotas', 'security_group_instance_association', 'security_group_rules', 'security_groups', - 'services', + 'services', 'migrations', 'users', 'user_project_association', 'user_project_role_association', 'user_role_association', diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py index 4b835c707..6a7621502 100644 --- a/nova/virt/xenapi/vmops.py +++ b/nova/virt/xenapi/vmops.py @@ -226,7 +226,7 @@ class VMOps(object): :param instance: the instance that owns the VHD in question :param dest: the destination host machine """ - + vm_ref = VMHelper.lookup(self._session, instance.name) def resize(self, instance, flavor): """Resize a running instance by changing it's RAM and disk size """ -- cgit From 49e07d0581317daf1bb605d56575c62743a210be Mon Sep 17 00:00:00 2001 From: Cerberus Date: Tue, 8 Feb 2011 11:07:03 -0600 Subject: Commas help --- nova/db/sqlalchemy/migrate_repo/versions/003_cactus.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/nova/db/sqlalchemy/migrate_repo/versions/003_cactus.py b/nova/db/sqlalchemy/migrate_repo/versions/003_cactus.py index bbe5cbcb0..dc384fbc3 100644 --- a/nova/db/sqlalchemy/migrate_repo/versions/003_cactus.py +++ b/nova/db/sqlalchemy/migrate_repo/versions/003_cactus.py @@ -27,10 +27,11 @@ meta = MetaData() # migrations = Table('migrations', meta, - Column('source_host', String(255)) - Column('dest_host', String(255)) - Column('instance_id', Integer, ForeignKey('instances.id'), nullable=True) - Column('status', String(255)) + Column('source_host', String(255)), + Column('dest_host', String(255)), + Column('instance_id', Integer, ForeignKey('instances.id'), + nullable=True), + Column('status', String(255)) ) def upgrade(migrate_engine): -- cgit From 089286802db0dca22cd67e46f26fab3ab0a3a73b Mon Sep 17 00:00:00 2001 From: Cerberus Date: Tue, 8 Feb 2011 13:12:21 -0600 Subject: Typos and primary keys --- nova/db/sqlalchemy/migrate_repo/versions/003_cactus.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/nova/db/sqlalchemy/migrate_repo/versions/003_cactus.py b/nova/db/sqlalchemy/migrate_repo/versions/003_cactus.py index dc384fbc3..4d01cd874 100644 --- a/nova/db/sqlalchemy/migrate_repo/versions/003_cactus.py +++ b/nova/db/sqlalchemy/migrate_repo/versions/003_cactus.py @@ -27,6 +27,7 @@ meta = MetaData() # migrations = Table('migrations', meta, + Column('id', Integer(), primary_key=True, nullable=False), Column('source_host', String(255)), Column('dest_host', String(255)), Column('instance_id', Integer, ForeignKey('instances.id'), @@ -38,7 +39,7 @@ def upgrade(migrate_engine): # Upgrade operations go here. Don't create your own engine; # bind migrate_engine to your metadata meta.bind = migrate_engine - for table in (migrations): + for table in (migrations, ): try: table.create() except Exception: -- cgit From ffc788fb41bf5a4bcb85cfa80b3437ed94d46291 Mon Sep 17 00:00:00 2001 From: Ken Pepple Date: Tue, 8 Feb 2011 11:24:05 -0800 Subject: additional error checking for nova-manage instance_type --- bin/nova-manage | 57 +++++++++++++++++++++++++++++------------------ nova/db/sqlalchemy/api.py | 26 +++++++++++++++------ 2 files changed, 54 insertions(+), 29 deletions(-) diff --git a/bin/nova-manage b/bin/nova-manage index c3fa9cf3f..78591585d 100755 --- a/bin/nova-manage +++ b/bin/nova-manage @@ -617,49 +617,62 @@ class InstanceTypeCommands(object): arguments: name memory_mb vcpus local_gb""" # FIXME(kpepple) check for absurb arguments (?) for option in [memory, flavorid, local_gb, vcpus]: - if option <= 0: + if (option <= 0) or (option.__class__ == int): print "Instance type parameters must be positive \ - numbers: %s" % option + integers: %s" % option sys.exit(1) - db.instance_type_create(context.get_admin_context(), + try: + db.instance_type_create(context.get_admin_context(), dict(name=name, memory_mb=memory, vcpus=vcpus, local_gb=local_gb, flavorid=flavorid)) - print "%s created" % name - return + print "%s created" % name + except exception.DBError, e: + print "%s is already a defined instance types" % name + sys.exit(1) + except: + print "Unknown error" + sys.exit(1) def delete(self, name): """Marks instance types / flavors as deleted arguments: name""" if name == None: print "Instance type name must be supplied" - exit(1) + sys.exit(1) else: - records = db.instance_type_destroy(context.get_admin_context(),\ - name) - if records != 1: + try: + records = db.instance_type_destroy( + context.get_admin_context(), + name) + except exception.NotFound, e: sys.exit(1) - return def list(self, name=None): """Lists all instance types / flavors arguments: [name]""" ctxt = context.get_admin_context() if name == None: - instance_types = db.instance_type_get_all(ctxt) - if len(instance_types) < 1: + try: + instance_types = db.instance_type_get_all(ctxt) + if len(instance_types) < 1: + sys.exit(1) + for k, v in instance_types.iteritems(): + print "%s : %s memory(MB), %s vcpus, %s storage(GB)" % \ + (k, v["memory_mb"], + v["vcpus"], v["local_gb"]) + except exception.NotFound, e: + print e sys.exit(1) - for k, v in instance_types.iteritems(): - print "%s : %s memory(MB), %s vcpus, %s storage(GB)" % \ - (k, v["memory_mb"], - v["vcpus"], v["local_gb"]) else: - instance_types = db.instance_type_get_by_name(ctxt, name) - print "%s : %s memory(MB), %s vcpus, %s storage(GB)" % \ - (instance_types["name"], instance_types["memory_mb"], - instance_types["vcpus"], instance_types["local_gb"]) - - return + try: + instance_types = db.instance_type_get_by_name(ctxt, name) + print "%s : %s memory(MB), %s vcpus, %s storage(GB)" % \ + (instance_types["name"], instance_types["memory_mb"], + instance_types["vcpus"], instance_types["local_gb"]) + except exception.NotFound, e: + print e + sys.exit(1) CATEGORIES = [ diff --git a/nova/db/sqlalchemy/api.py b/nova/db/sqlalchemy/api.py index 6065af9ca..f084423e1 100644 --- a/nova/db/sqlalchemy/api.py +++ b/nova/db/sqlalchemy/api.py @@ -2044,10 +2044,13 @@ def instance_type_get_all(context): filter_by(deleted=0).\ order_by("name").\ all() - inst_dict = {} - for i in inst_types: - inst_dict[i['name']] = dict(i) - return inst_dict + if inst_types: + inst_dict = {} + for i in inst_types: + inst_dict[i['name']] = dict(i) + return inst_dict + else: + raise exception.NotFound @require_context @@ -2057,7 +2060,10 @@ def instance_type_get_by_name(context, name): inst_type = session.query(models.InstanceTypes).\ filter_by(name=name).\ first() - return dict(inst_type) + if not inst_type: + raise exception.NotFound(_("No instance type with name %s") % name) + else: + return dict(inst_type) @require_context @@ -2067,7 +2073,10 @@ def instance_type_get_by_flavor_id(context, id): inst_type = session.query(models.InstanceTypes).\ filter_by(flavorid=int(id)).\ first() - return dict(inst_type) + if not inst_type: + raise exception.NotFound(_("No flavor with name %s") % id) + else: + return dict(inst_type) @require_admin_context @@ -2077,4 +2086,7 @@ def instance_type_destroy(context, name): instance_type_ref = session.query(models.InstanceTypes).\ filter_by(name=name) rows = instance_type_ref.update(dict(deleted=1)) - return rows + if not rows: + raise exception.NotFound(_("Couldn't delete instance type %s") % name) + else: + return rows -- cgit From 2f5d8a25c99875838a08ed06728bcd9d68cdd7f1 Mon Sep 17 00:00:00 2001 From: Ken Pepple Date: Tue, 8 Feb 2011 11:31:20 -0800 Subject: added testing for nova-manage instance_type --- nova/tests/test_nova_manage.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/nova/tests/test_nova_manage.py b/nova/tests/test_nova_manage.py index 487f172ff..5645bf97e 100644 --- a/nova/tests/test_nova_manage.py +++ b/nova/tests/test_nova_manage.py @@ -66,6 +66,17 @@ class NovaManageTestCase(test.TestCase): "120", "1"], stdout=fnull) self.assertEqual(1, retcode) + def test_should_fail_on_duplicate_name(self): + fnull = open(os.devnull, 'w') + retcode = subprocess.call(["bin/nova-manage", "instance_type",\ + "create", "fsfsfsdfsdf", "256", "1",\ + "120", "189"], stdout=fnull) + self.assertEqual(0, retcode) + retcode = subprocess.call(["bin/nova-manage", "instance_type",\ + "create", "fsfsfsdfsdf", "256", "1",\ + "120", "190"], stdout=fnull) + self.assertEqual(1, retcode) + def test_instance_type_delete_should_fail_without_valid_name(self): fnull = open(os.devnull, 'w') retcode = subprocess.call(["bin/nova-manage", "instance_type",\ -- cgit From cf562efb7441a761fcebf0653e4a886655826a10 Mon Sep 17 00:00:00 2001 From: Ken Pepple Date: Tue, 8 Feb 2011 15:03:35 -0800 Subject: added create and delete methods to instance_types in preparation to call them from nova-manage --- nova/compute/instance_types.py | 39 ++++++++++++++++++++++++++------------- 1 file changed, 26 insertions(+), 13 deletions(-) diff --git a/nova/compute/instance_types.py b/nova/compute/instance_types.py index 0e20982e3..af097672e 100644 --- a/nova/compute/instance_types.py +++ b/nova/compute/instance_types.py @@ -27,13 +27,28 @@ from nova import flags from nova import exception FLAGS = flags.FLAGS -# FIXME(kpepple) for dynamic flavors -# INSTANCE_TYPES = { -# 'm1.tiny': dict(memory_mb=512, vcpus=1, local_gb=0, flavorid=1), -# 'm1.small': dict(memory_mb=2048, vcpus=1, local_gb=20, flavorid=2), -# 'm1.medium': dict(memory_mb=4096, vcpus=2, local_gb=40, flavorid=3), -# 'm1.large': dict(memory_mb=8192, vcpus=4, local_gb=80, flavorid=4), -# 'm1.xlarge': dict(memory_mb=16384, vcpus=8, local_gb=160, flavorid=5)} + + +def create(name, memory, vcpus, local_gb, flavorid): + """Creates instance types / flavors + arguments: name memory_mb vcpus local_gb""" + for option in [memory, flavorid, local_gb, vcpus]: + if (option <= 0) or (option.__class__ == int): + raise InvalidParameters + db.instance_type_create(context.get_admin_context(), + dict(name=name, memory_mb=memory, + vcpus=vcpus, local_gb=local_gb, + flavorid=flavorid)) + + +def delete(name): + """Marks instance types / flavors as deleted + arguments: name""" + if name == None: + raise InvalidParameters + else: + records = db.instance_type_destroy(context.get_admin_context(), + name) def get_all_types(): @@ -48,17 +63,15 @@ def get_all_flavors(): def get_by_type(instance_type): """retrieve instance_type details""" - # FIXME(kpepple) for dynamic flavors if instance_type is None: return FLAGS.default_instance_type try: ctxt = context.get_admin_context() inst_type = db.instance_type_get_by_name(ctxt, instance_type) - except Exception, e: - print e + return inst_type['name'] + except exception.DBError: raise exception.ApiError(_("Unknown instance type: %s"), instance_type) - return inst_type['name'] def get_by_flavor_id(flavor_id): @@ -68,7 +81,7 @@ def get_by_flavor_id(flavor_id): try: ctxt = context.get_admin_context() flavor = db.instance_type_get_by_flavor_id(ctxt, flavor_id) - except Exception, e: + return flavor['name'] + except exception.DBError: raise exception.ApiError(_("Unknown flavor: %s"), flavor_id) - return flavor['name'] -- cgit From dd2544345e1686ee1ed020fd8f14607d10d8b3d1 Mon Sep 17 00:00:00 2001 From: Ken Pepple Date: Tue, 8 Feb 2011 19:24:14 -0800 Subject: added testing for instance_types.py and refactored nova-manage to use instance_types.py instead of going directly to db. --- bin/nova-manage | 63 ++++++++++++++++++------------------ nova/compute/instance_types.py | 31 ++++++++++++++---- nova/db/sqlalchemy/api.py | 9 ++++-- nova/tests/test_instance_types.py | 67 +++++++++++++++++++++++++++++++++++++++ nova/tests/test_nova_manage.py | 4 +-- 5 files changed, 129 insertions(+), 45 deletions(-) create mode 100644 nova/tests/test_instance_types.py diff --git a/bin/nova-manage b/bin/nova-manage index 78591585d..3ee188593 100755 --- a/bin/nova-manage +++ b/bin/nova-manage @@ -84,6 +84,7 @@ from nova import utils from nova.api.ec2.cloud import ec2_id_to_id from nova.auth import manager from nova.cloudpipe import pipelib +from nova.compute import instance_types from nova.db import migration @@ -612,67 +613,63 @@ class VolumeCommands(object): class InstanceTypeCommands(object): """Class for managing instance types / flavors.""" + def _print_instance_types(self, n, val): + print "%s: %s memory(MB), %s vcpus, %s storage(GB), %s flavorid"\ + % (n, val["memory_mb"], val["vcpus"], + val["local_gb"], val["flavorid"]) + def create(self, name, memory, vcpus, local_gb, flavorid): """Creates instance types / flavors arguments: name memory_mb vcpus local_gb""" - # FIXME(kpepple) check for absurb arguments (?) - for option in [memory, flavorid, local_gb, vcpus]: - if (option <= 0) or (option.__class__ == int): - print "Instance type parameters must be positive \ - integers: %s" % option - sys.exit(1) try: - db.instance_type_create(context.get_admin_context(), - dict(name=name, memory_mb=memory, - vcpus=vcpus, local_gb=local_gb, - flavorid=flavorid)) - print "%s created" % name + instance_types.create(name, memory, vcpus, local_gb, flavorid) + except exception.InvalidInputException, e: + print "Must supply valid parameters to create instance type" + sys.exit(1) except exception.DBError, e: - print "%s is already a defined instance types" % name + print "DB Error: %s" % e sys.exit(1) except: print "Unknown error" sys.exit(1) + else: + print "%s created" % name def delete(self, name): """Marks instance types / flavors as deleted arguments: name""" - if name == None: - print "Instance type name must be supplied" + try: + records = instance_types.destroy(name) + except exception.InvalidParameters: + print "Valid instance type name is required" + sys.exit(1) + except exception.NotFound, e: + print "Instance type name %s not found. \ + No instance type deleted." % name sys.exit(1) else: - try: - records = db.instance_type_destroy( - context.get_admin_context(), - name) - except exception.NotFound, e: - sys.exit(1) + print "%s deleted" % name def list(self, name=None): - """Lists all instance types / flavors + """Lists all or specific instance types / flavors arguments: [name]""" - ctxt = context.get_admin_context() if name == None: try: - instance_types = db.instance_type_get_all(ctxt) - if len(instance_types) < 1: - sys.exit(1) - for k, v in instance_types.iteritems(): - print "%s : %s memory(MB), %s vcpus, %s storage(GB)" % \ - (k, v["memory_mb"], - v["vcpus"], v["local_gb"]) + inst_types = instance_types.get_all_types() except exception.NotFound, e: print e sys.exit(1) + else: + for k, v in inst_types.iteritems(): + self._print_instance_types(k, v) else: try: - instance_types = db.instance_type_get_by_name(ctxt, name) - print "%s : %s memory(MB), %s vcpus, %s storage(GB)" % \ - (instance_types["name"], instance_types["memory_mb"], - instance_types["vcpus"], instance_types["local_gb"]) + inst_types = instance_types.get_instance_type(name) except exception.NotFound, e: print e sys.exit(1) + else: + self._print_instance_types(name, inst_types) CATEGORIES = [ diff --git a/nova/compute/instance_types.py b/nova/compute/instance_types.py index af097672e..b97a0da25 100644 --- a/nova/compute/instance_types.py +++ b/nova/compute/instance_types.py @@ -32,33 +32,50 @@ FLAGS = flags.FLAGS def create(name, memory, vcpus, local_gb, flavorid): """Creates instance types / flavors arguments: name memory_mb vcpus local_gb""" - for option in [memory, flavorid, local_gb, vcpus]: - if (option <= 0) or (option.__class__ == int): - raise InvalidParameters + for option in [memory, flavorid, vcpus]: + if option <= 0: + raise exception.InvalidInputException("Parameters incorrect") db.instance_type_create(context.get_admin_context(), dict(name=name, memory_mb=memory, vcpus=vcpus, local_gb=local_gb, flavorid=flavorid)) -def delete(name): +def destroy(name): """Marks instance types / flavors as deleted arguments: name""" if name == None: - raise InvalidParameters + raise exception.InvalidInputException else: records = db.instance_type_destroy(context.get_admin_context(), name) + if records == 0: + raise exception.NotFound("Cannot find instance type named %s" % name) + else: + return records def get_all_types(): """retrieves all instance_types""" - return db.instance_type_get_all() + return db.instance_type_get_all(context.get_admin_context()) def get_all_flavors(): """retrieves all flavors. alias for instance_types.get_all_types()""" - return get_all_types() + return get_all_types(context.get_admin_context()) + + +def get_instance_type(name): + """Retrieves single instance type by name""" + if name is None: + return FLAGS.default_instance_type + try: + ctxt = context.get_admin_context() + inst_type = db.instance_type_get_by_name(ctxt, name) + return inst_type + except exception.DBError: + raise exception.ApiError(_("Unknown instance type: %s"), + instance_type) def get_by_type(instance_type): diff --git a/nova/db/sqlalchemy/api.py b/nova/db/sqlalchemy/api.py index f084423e1..e5afb8eac 100644 --- a/nova/db/sqlalchemy/api.py +++ b/nova/db/sqlalchemy/api.py @@ -2025,9 +2025,12 @@ def console_get(context, console_id, instance_id=None): @require_admin_context def instance_type_create(context, values): - instance_type_ref = models.InstanceTypes() - instance_type_ref.update(values) - instance_type_ref.save() + try: + instance_type_ref = models.InstanceTypes() + instance_type_ref.update(values) + instance_type_ref.save() + except: + raise exception.DBError return instance_type_ref diff --git a/nova/tests/test_instance_types.py b/nova/tests/test_instance_types.py new file mode 100644 index 000000000..819431763 --- /dev/null +++ b/nova/tests/test_instance_types.py @@ -0,0 +1,67 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# 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. +""" +Unit Tests for instance types code +""" +import datetime + +from nova import context +from nova import db +from nova import exception +from nova import flags +from nova import log as logging +from nova import test +from nova import utils +from nova.compute import instance_types +from nova.db.sqlalchemy.session import get_session +from nova.db.sqlalchemy import models + +FLAGS = flags.FLAGS +LOG = logging.getLogger('nova.tests.compute') + + +class InstanceTypeTestCase(test.TestCase): + """Test cases for instance type code""" + def setUp(self): + super(InstanceTypeTestCase, self).setUp() + session = get_session() + max_flavorid = session.query(models.InstanceTypes).\ + order_by("flavorid desc").first() + self.flavorid = max_flavorid["flavorid"] + 1 + self.name = str(datetime.datetime.utcnow()) + + def tearDown(self): + pass + + def test_instance_type_create_then_delete(self): + """Ensure instance types can be created""" + starting_inst_list = instance_types.get_all_types() + instance_types.create(self.name, 256, 1, 120, self.flavorid) + new = instance_types.get_all_types() + self.assertNotEqual(len(starting_inst_list), + len(new), + 'instance was not created') + rows = instance_types.destroy(self.name) + self.assertEqual(rows, 1) + self.assertEqual(1, + instance_types.get_instance_type(self.name)["deleted"]) + self.assertEqual(starting_inst_list, instance_types.get_all_types()) + + def test_get_all_instance_types(self): + """Ensures that all instance types can be retrieved""" + session = get_session() + total_instance_types = session.query(models.InstanceTypes).\ + count() + inst_types = instance_types.get_all_types() + self.assertEqual(total_instance_types, len(inst_types)) diff --git a/nova/tests/test_nova_manage.py b/nova/tests/test_nova_manage.py index 5645bf97e..a2fa919a0 100644 --- a/nova/tests/test_nova_manage.py +++ b/nova/tests/test_nova_manage.py @@ -31,8 +31,8 @@ class NovaManageTestCase(test.TestCase): def test_create_and_delete_instance_types(self): fnull = open(os.devnull, 'w') - retcode = subprocess.call(["bin/nova-manage", "instance_type",\ - "create", "test", "256", "1",\ + retcode = subprocess.call(["bin/nova-manage", "instance_type", + "create", "test", "256", "1", "120", "99"], stdout=fnull) self.assertEqual(0, retcode) retcode = subprocess.call(["bin/nova-manage", "instance_type",\ -- cgit From 99a02a7d68416c72675f7b6c554df9b682771e04 Mon Sep 17 00:00:00 2001 From: Ken Pepple Date: Wed, 9 Feb 2011 11:36:45 -0800 Subject: added support to pull list of ALL instance types even those that are marked deleted --- nova/compute/instance_types.py | 14 ++++++++------ nova/db/api.py | 4 ++-- nova/db/sqlalchemy/api.py | 6 +++--- 3 files changed, 13 insertions(+), 11 deletions(-) diff --git a/nova/compute/instance_types.py b/nova/compute/instance_types.py index b97a0da25..b13ccda43 100644 --- a/nova/compute/instance_types.py +++ b/nova/compute/instance_types.py @@ -55,13 +55,15 @@ def destroy(name): return records -def get_all_types(): - """retrieves all instance_types""" - return db.instance_type_get_all(context.get_admin_context()) +def get_all_types(inactive=0): + """Retrieves non-deleted instance_types. + Pass true as argument if you want deleted instance types returned also.""" + return db.instance_type_get_all(context.get_admin_context(), inactive) def get_all_flavors(): - """retrieves all flavors. alias for instance_types.get_all_types()""" + """retrieves non-deleted flavors. alias for instance_types.get_all_types(). + Pass true as argument if you want deleted instance types returned also.""" return get_all_types(context.get_admin_context()) @@ -79,7 +81,7 @@ def get_instance_type(name): def get_by_type(instance_type): - """retrieve instance_type details""" + """retrieve instance type name""" if instance_type is None: return FLAGS.default_instance_type try: @@ -92,7 +94,7 @@ def get_by_type(instance_type): def get_by_flavor_id(flavor_id): - """retrieve instance_type's name by flavor_id""" + """retrieve instance type's name by flavor_id""" if flavor_id is None: return FLAGS.default_instance_type try: diff --git a/nova/db/api.py b/nova/db/api.py index 2f1903ade..69f577764 100644 --- a/nova/db/api.py +++ b/nova/db/api.py @@ -995,9 +995,9 @@ def instance_type_create(context, values): return IMPL.instance_type_create(context, values) -def instance_type_get_all(context): +def instance_type_get_all(context, inactive=0): """Get all instance types""" - return IMPL.instance_type_get_all(context) + return IMPL.instance_type_get_all(context, inactive) def instance_type_get_by_name(context, name): diff --git a/nova/db/sqlalchemy/api.py b/nova/db/sqlalchemy/api.py index e5afb8eac..1e13e4d2d 100644 --- a/nova/db/sqlalchemy/api.py +++ b/nova/db/sqlalchemy/api.py @@ -2035,16 +2035,16 @@ def instance_type_create(context, values): @require_context -def instance_type_get_all(context): +def instance_type_get_all(context, inactive=0): """ - Returns a dict describing all instance_types with name as key: + Returns a dict describing all non-deleted instance_types with name as key: {'m1.tiny': dict(memory_mb=512, vcpus=1, local_gb=0, flavorid=1), 'm1.small': dict(memory_mb=2048, vcpus=1, local_gb=20, flavorid=2), 'm1.medium': dict(memory_mb=4096, vcpus=2, local_gb=40, flavorid=3)} """ session = get_session() inst_types = session.query(models.InstanceTypes).\ - filter_by(deleted=0).\ + filter_by(deleted=inactive).\ order_by("name").\ all() if inst_types: -- cgit From ce5e3bdd30712aa6704926e6cdeb5ae73ae8200b Mon Sep 17 00:00:00 2001 From: Cerberus Date: Wed, 9 Feb 2011 15:26:37 -0600 Subject: A lot of stuff --- nova/compute/manager.py | 8 ++-- nova/db/sqlalchemy/models.py | 1 + nova/virt/xenapi/vmops.py | 53 +++++++++++++++++----- nova/virt/xenapi_conn.py | 8 +++- .../xenapi/etc/xapi.d/plugins/data_transfer | 37 +++++++-------- 5 files changed, 71 insertions(+), 36 deletions(-) diff --git a/nova/compute/manager.py b/nova/compute/manager.py index 4189c49a4..ac09f7c8c 100644 --- a/nova/compute/manager.py +++ b/nova/compute/manager.py @@ -423,12 +423,12 @@ class ComputeManager(manager.Manager): migration_ref = self.db.migration_get(context, migration_id) self.db.migration_update(context, migration_id, { 'status': 'migrating', }) - self.driver.transfer_disk(context, instance_id, + + self.driver.migrate_disk_and_power_off(context, instance, migration_ref['dest_host']) + self.db.migration_update(context, migration_id, { 'status': 'post-migrating', }) - - self.driver.power_off(context, migration_ref['instance_id']) # This is where we would update the VM record after resizing service = self.db.service_get_by_host_and_topic(context, @@ -449,7 +449,7 @@ class ComputeManager(manager.Manager): migration_ref['instance_id']) # this may get passed into the following spawn instead - self.driver.attach_disk(context, migration_ref['instance_id']) + self.driver.attach_disk(context, instance_ref) self.driver.spawn(context, instance_ref, preexisting=True) self.db.migration_update(context, migration_id, diff --git a/nova/db/sqlalchemy/models.py b/nova/db/sqlalchemy/models.py index 499275504..ebf3a382b 100644 --- a/nova/db/sqlalchemy/models.py +++ b/nova/db/sqlalchemy/models.py @@ -369,6 +369,7 @@ class KeyPair(BASE, NovaBase): class Migration(BASE, NovaBase): """Represents a running host-to-host migration.""" __tablename__ = 'migrations' + id = Column(Integer, primary_key=True, nullable=False) source_host = Column(String(255)) dest_host = Column(String(255)) instance_id = Column(Integer, ForeignKey('instances.id'), nullable=True) diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py index 6a7621502..40b075b3d 100644 --- a/nova/virt/xenapi/vmops.py +++ b/nova/virt/xenapi/vmops.py @@ -196,6 +196,26 @@ class VMOps(object): Glance. """ + with self._get_snapshot(instance) as snapshot: + # call plugin to ship snapshot off to glance + VMHelper.upload_image( + self._session, instance.id, snapshot.vdi_uuids, image_id) + + logging.debug(_("Finished snapshot and upload for VM %s"), instance) + + def _get_snapshot(self, instance): + class Snapshot(object): + def __init__(self, virt, instance, vdis): + self.instance = instance + self.vdi_uuids = vdis + self.virt = virt + + def __enter__(self): + return self + + def __exit__(self, type, value, traceback): + self.virt._destroy(self.instance, self.vm_ref, shutdown=False) + #TODO(sirp): Add quiesce and VSS locking support when Windows support # is added @@ -204,30 +224,41 @@ class VMOps(object): label = "%s-snapshot" % instance.name try: - template_vm_ref, template_vdi_uuids = VMHelper.create_snapshot( + _, template_vdi_uuids = VMHelper.create_snapshot( self._session, instance.id, vm_ref, label) + return Snapshot(self, instance, template_vdi_uuids) except self.XenAPI.Failure, exc: logging.error(_("Unable to Snapshot %(vm_ref)s: %(exc)s") % locals()) return - try: - # call plugin to ship snapshot off to glance - VMHelper.upload_image( - self._session, instance.id, template_vdi_uuids, image_id) - finally: - self._destroy(instance, template_vm_ref, shutdown=False) - - logging.debug(_("Finished snapshot and upload for VM %s"), instance) - - def transfer_disk(self, instance, dest): + def migrate_disk_and_power_off(self, instance, dest): """ Copies a VHD from one host machine to another :param instance: the instance that owns the VHD in question :param dest: the destination host machine + :param disk_type: values are 'primary' or 'cow' """ vm_ref = VMHelper.lookup(self._session, instance.name) + # The primary VDI becomes the COW after the snapshot. We can figure + # this out from the VBD. The base copy is the parent_uuid returned + # from the snapshot creation + with self._get_snapshot(instance) as snapshot: + params = {'host':dest, 'vdi_uuid':snapshot.vdi_uuids[1]} + kwargs = {'params': pickle.dumps(params)} + self._session.async_call_plugin('data_transfer', 'transfer_vhd', + kwargs) + + # Now power down the instance and transfer the COW VHD + self._shutdown(instance, method='clean') + + _, vm_vdi_rec = get_vdi_for_vm_safely(session, vm_ref) + params = {'host':dest, 'vdi_uuid': vm_vdi_rec['uuid']} + kwargs = {'params': pickle.dumps(params)} + self._session.async_call_plugin('data_transfer', 'transfer_vhd', + kwargs) + def resize(self, instance, flavor): """Resize a running instance by changing it's RAM and disk size """ raise NotImplementedError() diff --git a/nova/virt/xenapi_conn.py b/nova/virt/xenapi_conn.py index 2e587117a..98b5e7851 100644 --- a/nova/virt/xenapi_conn.py +++ b/nova/virt/xenapi_conn.py @@ -192,8 +192,14 @@ class XenAPIConnection(object): """powers on a powered off VM instance""" self._vmops.power_on(instance) - def transfer_disk(self, instance, dest, callback): + def migrate_disk_and_power_off(self, instance, dest): + """Transfers the VHD of a running instance to another host, then shuts + off the instance copies over the COW disk""" self._vmops.transfer_disk(instance, dest) + + def move_disk(self, instance_ref): + """Moves the copied VDIs into the SR""" + pass def suspend(self, instance, callback): """suspend the specified instance""" diff --git a/plugins/xenserver/xenapi/etc/xapi.d/plugins/data_transfer b/plugins/xenserver/xenapi/etc/xapi.d/plugins/data_transfer index 2af4a758b..bd46e1c0b 100644 --- a/plugins/xenserver/xenapi/etc/xapi.d/plugins/data_transfer +++ b/plugins/xenserver/xenapi/etc/xapi.d/plugins/data_transfer @@ -21,6 +21,7 @@ XenAPI Plugin for transfering data between host nodes """ import os.path +import pickle import subprocess import XenAPIPlugin @@ -30,27 +31,23 @@ DEVNULL = '/dev/null' KEYSCAN = '/usr/bin/ssh-keyscan' RSYNC = '/usr/bin/rsync' -def _key_scan_and_add(host): - """SSH scans a remote host and writes the SSH key out to known_hosts""" - # Touch the file if it doesn't yet exist - open(SSH_HOSTS, 'a').close() - - null = open(DEVNULL, 'w') - known_hosts = open(SSH_HOSTS, 'a') - key = subprocess.Popen(['/usr/bin/ssh-keyscan', '-t', 'rsa', host], - stdout=subprocess.PIPE, stderr=null).communicate()[0].strip() - grep = subprocess.call(['/bin/grep', '-o', '%s' % key, SSH_HOSTS], - stdout=null, stderr=null) - if grep == 1: - known_hosts.write(key) - null.close() - known_hosts.close() - -def transfer_file(host, file_path): + +def transfer_vhd(session, args): """Rsyncs a VHD to an adjacent host""" - _key_scan_and_add(host) - if subprocess.call([RSYNC, file_path, "%s:/root/" % host]) != 0: + params = pickle.dumps(args) + instance_id = params['instance_id'] + host = params['host'] + vdi_uuid = params['vdi_uuid'] + sr_path = get_sr_path(session) + vhd_path = "%s.vhd" % vdi_uuid + + source_path = "%s/%s" % (sr_path, vhd_path) + dest_path = '%s:/images/instance%d/' % (host, instance_id) + rsync_args = [['nohup', RSYNC, '-av', '--progress', + '-e "ssh -o StrictHostKeyChecking=no"', source_path, dest_path] + + if subprocess.call(rsync_args) != 0: raise Exception("Unexpected VHD transfer failure") if __name__ == '__main__': - XenAPIPlugin.dispatch({'transfer_file': transfer_file}) + XenAPIPlugin.dispatch({'transfer_vhd': transfer_vhd}) -- cgit From 482c7b57a3d0ac8bf6df98539bf8a1220470e0f7 Mon Sep 17 00:00:00 2001 From: Cerberus Date: Wed, 9 Feb 2011 15:27:23 -0600 Subject: Renamed migration plugin --- .../xenapi/etc/xapi.d/plugins/data_transfer | 53 ---------------------- .../xenserver/xenapi/etc/xapi.d/plugins/migration | 53 ++++++++++++++++++++++ 2 files changed, 53 insertions(+), 53 deletions(-) delete mode 100644 plugins/xenserver/xenapi/etc/xapi.d/plugins/data_transfer create mode 100644 plugins/xenserver/xenapi/etc/xapi.d/plugins/migration diff --git a/plugins/xenserver/xenapi/etc/xapi.d/plugins/data_transfer b/plugins/xenserver/xenapi/etc/xapi.d/plugins/data_transfer deleted file mode 100644 index bd46e1c0b..000000000 --- a/plugins/xenserver/xenapi/etc/xapi.d/plugins/data_transfer +++ /dev/null @@ -1,53 +0,0 @@ -#!/usr/bin/env python - -# 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. - -""" -XenAPI Plugin for transfering data between host nodes -""" - -import os.path -import pickle -import subprocess - -import XenAPIPlugin - -SSH_HOSTS = '/root/.ssh/known_hosts' -DEVNULL = '/dev/null' -KEYSCAN = '/usr/bin/ssh-keyscan' -RSYNC = '/usr/bin/rsync' - - -def transfer_vhd(session, args): - """Rsyncs a VHD to an adjacent host""" - params = pickle.dumps(args) - instance_id = params['instance_id'] - host = params['host'] - vdi_uuid = params['vdi_uuid'] - sr_path = get_sr_path(session) - vhd_path = "%s.vhd" % vdi_uuid - - source_path = "%s/%s" % (sr_path, vhd_path) - dest_path = '%s:/images/instance%d/' % (host, instance_id) - rsync_args = [['nohup', RSYNC, '-av', '--progress', - '-e "ssh -o StrictHostKeyChecking=no"', source_path, dest_path] - - if subprocess.call(rsync_args) != 0: - raise Exception("Unexpected VHD transfer failure") - -if __name__ == '__main__': - XenAPIPlugin.dispatch({'transfer_vhd': transfer_vhd}) diff --git a/plugins/xenserver/xenapi/etc/xapi.d/plugins/migration b/plugins/xenserver/xenapi/etc/xapi.d/plugins/migration new file mode 100644 index 000000000..bd46e1c0b --- /dev/null +++ b/plugins/xenserver/xenapi/etc/xapi.d/plugins/migration @@ -0,0 +1,53 @@ +#!/usr/bin/env python + +# 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. + +""" +XenAPI Plugin for transfering data between host nodes +""" + +import os.path +import pickle +import subprocess + +import XenAPIPlugin + +SSH_HOSTS = '/root/.ssh/known_hosts' +DEVNULL = '/dev/null' +KEYSCAN = '/usr/bin/ssh-keyscan' +RSYNC = '/usr/bin/rsync' + + +def transfer_vhd(session, args): + """Rsyncs a VHD to an adjacent host""" + params = pickle.dumps(args) + instance_id = params['instance_id'] + host = params['host'] + vdi_uuid = params['vdi_uuid'] + sr_path = get_sr_path(session) + vhd_path = "%s.vhd" % vdi_uuid + + source_path = "%s/%s" % (sr_path, vhd_path) + dest_path = '%s:/images/instance%d/' % (host, instance_id) + rsync_args = [['nohup', RSYNC, '-av', '--progress', + '-e "ssh -o StrictHostKeyChecking=no"', source_path, dest_path] + + if subprocess.call(rsync_args) != 0: + raise Exception("Unexpected VHD transfer failure") + +if __name__ == '__main__': + XenAPIPlugin.dispatch({'transfer_vhd': transfer_vhd}) -- cgit From cf2db4f18dbff14fb8882a4747c607ff26b1de55 Mon Sep 17 00:00:00 2001 From: Ken Pepple Date: Wed, 9 Feb 2011 13:58:43 -0800 Subject: fixed overlooked mandatory changes in Xen --- nova/virt/libvirt_conn.py | 5 +++-- nova/virt/xenapi/vm_utils.py | 4 ++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py index 94fe93c40..14cc4cf39 100644 --- a/nova/virt/libvirt_conn.py +++ b/nova/virt/libvirt_conn.py @@ -612,7 +612,7 @@ class LibvirtConnection(object): user=user, project=project, size=size) - type_data = test.INSTANCE_TYPES[inst['instance_type']] + type_data = instance_types.get_instance_type([inst['instance_type']]) if type_data['local_gb']: self._cache_image(fn=self._create_local, @@ -673,7 +673,8 @@ class LibvirtConnection(object): instance['id']) # FIXME(vish): stick this in db instance_type = instance['instance_type'] - instance_type = test.INSTANCE_TYPES[instance_type] + # instance_type = test.INSTANCE_TYPES[instance_type] + instance_type = instance_types.get_instance_type(instance_type) ip_address = db.instance_get_fixed_address(context.get_admin_context(), instance['id']) # Assume that the gateway also acts as the dhcp server. diff --git a/nova/virt/xenapi/vm_utils.py b/nova/virt/xenapi/vm_utils.py index 38a6f73ce..a9308eea1 100644 --- a/nova/virt/xenapi/vm_utils.py +++ b/nova/virt/xenapi/vm_utils.py @@ -31,7 +31,6 @@ import glance.client from nova import exception from nova import flags from nova import log as logging -from nova import test from nova import utils from nova.auth.manager import AuthManager from nova.compute import instance_types @@ -83,7 +82,8 @@ class VMHelper(HelperBase): the pv_kernel flag indicates whether the guest is HVM or PV """ - instance_type = test.INSTANCE_TYPES[instance.instance_type] + instance_type = instance_types.\ + get_instance_type(instance.instance_type) mem = str(long(instance_type['memory_mb']) * 1024 * 1024) vcpus = str(instance_type['vcpus']) rec = { -- cgit From ac33f61c5c382fc7c8e8ab872192858860672d70 Mon Sep 17 00:00:00 2001 From: Cerberus Date: Wed, 9 Feb 2011 16:38:27 -0600 Subject: Plugin tidying and more migration implementation --- nova/virt/xenapi/vm_utils.py | 16 +++--- nova/virt/xenapi/vmops.py | 35 +++++++---- nova/virt/xenapi_conn.py | 9 ++- plugins/xenserver/xenapi/etc/xapi.d/plugins/glance | 4 +- .../xenserver/xenapi/etc/xapi.d/plugins/migration | 67 +++++++++++++++++++++- 5 files changed, 108 insertions(+), 23 deletions(-) diff --git a/nova/virt/xenapi/vm_utils.py b/nova/virt/xenapi/vm_utils.py index 4bbd522c1..e16662aad 100644 --- a/nova/virt/xenapi/vm_utils.py +++ b/nova/virt/xenapi/vm_utils.py @@ -478,6 +478,14 @@ class VMHelper(HelperBase): except cls.XenAPI.Failure as e: return {"Unable to retrieve diagnostics": e} + @classmethod + def scan_sr(cls, session, instance_id, sr_ref): + LOG.debug(_("Re-scanning SR %s"), sr_ref) + task = session.call_xenapi('Async.SR.scan', sr_ref) + session.wait_for_task(instance_id, task) + + + def get_rrd(host, uuid): """Return the VM RRD XML as a string""" @@ -520,12 +528,6 @@ def get_vhd_parent_uuid(session, vdi_ref): return None -def scan_sr(session, instance_id, sr_ref): - LOG.debug(_("Re-scanning SR %s"), sr_ref) - task = session.call_xenapi('Async.SR.scan', sr_ref) - session.wait_for_task(instance_id, task) - - def wait_for_vhd_coalesce(session, instance_id, sr_ref, vdi_ref, original_parent_uuid): """ Spin until the parent VHD is coalesced into its parent VHD @@ -550,7 +552,7 @@ def wait_for_vhd_coalesce(session, instance_id, sr_ref, vdi_ref, " %(max_attempts)d), giving up...") % locals()) raise exception.Error(msg) - scan_sr(session, instance_id, sr_ref) + VMHelper.scan_sr(session, instance_id, sr_ref) parent_uuid = get_vhd_parent_uuid(session, vdi_ref) if original_parent_uuid and (parent_uuid != original_parent_uuid): LOG.debug(_("Parent %(parent_uuid)s doesn't match original parent" diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py index 40b075b3d..ea4b7899b 100644 --- a/nova/virt/xenapi/vmops.py +++ b/nova/virt/xenapi/vmops.py @@ -146,7 +146,7 @@ class VMOps(object): """ vm = None try: - if instance_or_vm.startswith("OpaqueRef:"): + if instance_or_vm.startswith("OpaqueRef:") # Got passed an opaque ref; return it return instance_or_vm else: @@ -241,23 +241,38 @@ class VMOps(object): """ vm_ref = VMHelper.lookup(self._session, instance.name) - # The primary VDI becomes the COW after the snapshot. We can figure - # this out from the VBD. The base copy is the parent_uuid returned + # The primary VDI becomes the COW after the snapshot, and we can + # identify it via the VBD. The base copy is the parent_uuid returned # from the snapshot creation + + #TODO(mdietz): explicitly forcing the base_copy and cow names is + #pretty fugly with self._get_snapshot(instance) as snapshot: - params = {'host':dest, 'vdi_uuid':snapshot.vdi_uuids[1]} - kwargs = {'params': pickle.dumps(params)} - self._session.async_call_plugin('data_transfer', 'transfer_vhd', - kwargs) + params = {'host':dest, 'vdi_uuid':snapshot.vdi_uuids[1], + 'dest_name':'base_copy.vhd'} + self._session.async_call_plugin('migration', 'transfer_vhd', + {'params': pickle.dumps(params)}) # Now power down the instance and transfer the COW VHD self._shutdown(instance, method='clean') _, vm_vdi_rec = get_vdi_for_vm_safely(session, vm_ref) - params = {'host':dest, 'vdi_uuid': vm_vdi_rec['uuid']} - kwargs = {'params': pickle.dumps(params)} + params = {'host':dest, 'vdi_uuid': vm_vdi_rec['uuid'], + 'dest_name': 'cow.vhd'} self._session.async_call_plugin('data_transfer', 'transfer_vhd', - kwargs) + {'params': pickle.dumps(params)}) + return snapshot.vdi_uuids[1], vm_vdi_rec['uuid'] + + def attach_disk(self, instance): + vm_ref = VMHelper.lookup(self._session, instance.name) + + params = { 'instance_id': instance.id } + self._session.async_call_plugin('migration', 'move_vhds_into_sr', + {'params': pickle.dumps(params)}) + + + _, vm_vdi_rec = get_vdi_for_vm_safely(session, vm_ref) + VMHelper.scan_sr(self._session, instance.id, vm_vdi_rec['SR']) def resize(self, instance, flavor): """Resize a running instance by changing it's RAM and disk size """ diff --git a/nova/virt/xenapi_conn.py b/nova/virt/xenapi_conn.py index 98b5e7851..cc43050b4 100644 --- a/nova/virt/xenapi_conn.py +++ b/nova/virt/xenapi_conn.py @@ -164,6 +164,9 @@ class XenAPIConnection(object): """Resize a VM instance""" raise NotImplementedError() + def attach_disk(self, instance_ref): + + def reboot(self, instance): """Reboot VM instance""" self._vmops.reboot(instance) @@ -195,11 +198,11 @@ class XenAPIConnection(object): def migrate_disk_and_power_off(self, instance, dest): """Transfers the VHD of a running instance to another host, then shuts off the instance copies over the COW disk""" - self._vmops.transfer_disk(instance, dest) + self._vmops.migrate_disk_and_power_off(instance, dest) - def move_disk(self, instance_ref): + def attach_disk(self, instance): """Moves the copied VDIs into the SR""" - pass + self._vmops.attach_disk(instance) def suspend(self, instance, callback): """suspend the specified instance""" diff --git a/plugins/xenserver/xenapi/etc/xapi.d/plugins/glance b/plugins/xenserver/xenapi/etc/xapi.d/plugins/glance index aadacce57..817269769 100644 --- a/plugins/xenserver/xenapi/etc/xapi.d/plugins/glance +++ b/plugins/xenserver/xenapi/etc/xapi.d/plugins/glance @@ -138,8 +138,8 @@ def get_sr_path(session): return sr_path -#TODO(sirp): both objectstore and glance need this, should this be refactored -#into common lib +#TODO(sirp): objectstore, migration and glance need this, should this be +# refactored into common lib def find_sr(session): host = get_this_host(session) srs = session.xenapi.SR.get_all() diff --git a/plugins/xenserver/xenapi/etc/xapi.d/plugins/migration b/plugins/xenserver/xenapi/etc/xapi.d/plugins/migration index bd46e1c0b..e81b18a5e 100644 --- a/plugins/xenserver/xenapi/etc/xapi.d/plugins/migration +++ b/plugins/xenserver/xenapi/etc/xapi.d/plugins/migration @@ -20,16 +20,79 @@ XenAPI Plugin for transfering data between host nodes """ +import os import os.path import pickle +import shutil import subprocess +import uuid import XenAPIPlugin +from pluginlib_nova import * + SSH_HOSTS = '/root/.ssh/known_hosts' DEVNULL = '/dev/null' KEYSCAN = '/usr/bin/ssh-keyscan' RSYNC = '/usr/bin/rsync' +FILE_SR_PATH = '/var/run/sr-mount' +IMAGE_PATH = '/images/' +VHD_UTIL = '/usr/sbin/vhd-util' + +def get_sr_path(session): + sr_ref = find_sr(session) + + if sr_ref is None: + raise Exception('Cannot find SR to read VDI from') + + sr_rec = session.xenapi.SR.get_record(sr_ref) + sr_uuid = sr_rec["uuid"] + sr_path = os.path.join(FILE_SR_PATH, sr_uuid) + return sr_path + +def find_sr(session): + host = get_this_host(session) + srs = session.xenapi.SR.get_all() + for sr in srs: + sr_rec = session.xenapi.SR.get_record(sr) + if not ('i18n-key' in sr_rec['other_config'] and + sr_rec['other_config']['i18n-key'] == 'local-storage'): + continue + for pbd in sr_rec['PBDs']: + pbd_rec = session.xenapi.PBD.get_record(pbd) + if pbd_rec['host'] == host: + return sr + return None + +def move_vhds_into_sr(session, args): + """Moves the VHDs from their copied location to the SR""" + params = pickle.dumps(args) + instance_id = params['instance_id'] + + sr_path = get_sr_path(session) + + # Discover the copied VHDs locally, and then set up paths to copy + # them to under the SR + source_image_path = "%s/instance%d" % (IMAGE_PATH, instance_id) + source_base_copy_path = "%s/base_copy.vhd" % source_image_path + source_cow_path = "%s/cow.vhd" % source_image_path + + temp_vhd_path = "%s/instance%d/" % (sr_path, instance_id) + new_base_copy_path = "%s/%s.vhd" % (temp_vhd_path, str(uuid.uuid4())) + new_cow_path = "%s/%s.vhd" % (temp_vhd_path, str(uuid.uuid4())) + + os.mkdir(temp_vhd_path) + shutil.move(source_base_copy_path, new_base_copy_path) + shutil.move(source_cow_path, new_cow_path) + + os.rmdir(source_image_path) + + # Link the COW to the base copy + subprocess.call([VHD_UTIL, 'modify', '-n', new_cow_path, '-p', + new_base_copy_path]) + + shutil.move("%s/*.vhd" % temp_vhd_path, sr_path) + os.rmdir(temp_vhd_path) def transfer_vhd(session, args): @@ -38,11 +101,13 @@ def transfer_vhd(session, args): instance_id = params['instance_id'] host = params['host'] vdi_uuid = params['vdi_uuid'] + dest_name = params['dest_name'] sr_path = get_sr_path(session) vhd_path = "%s.vhd" % vdi_uuid source_path = "%s/%s" % (sr_path, vhd_path) - dest_path = '%s:/images/instance%d/' % (host, instance_id) + dest_path = '%s:%sinstance%d/%s' % (host, IMAGE_PATH, instance_id, + dest_name) rsync_args = [['nohup', RSYNC, '-av', '--progress', '-e "ssh -o StrictHostKeyChecking=no"', source_path, dest_path] -- cgit From 1a9225d945bdc9b94473c1dd4ad5b9e4b7624571 Mon Sep 17 00:00:00 2001 From: Ken Pepple Date: Wed, 9 Feb 2011 15:48:31 -0800 Subject: forgot to register new instance_types table --- nova/db/sqlalchemy/models.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nova/db/sqlalchemy/models.py b/nova/db/sqlalchemy/models.py index 44583861b..3f418392c 100644 --- a/nova/db/sqlalchemy/models.py +++ b/nova/db/sqlalchemy/models.py @@ -550,7 +550,7 @@ def register_models(): connection is lost and needs to be reestablished. """ from sqlalchemy import create_engine - models = (Service, Instance, InstanceActions, + models = (Service, Instance, InstanceActions, InstanceTypes, Volume, ExportDevice, IscsiTarget, FixedIp, FloatingIp, Network, SecurityGroup, SecurityGroupIngressRule, SecurityGroupInstanceAssociation, AuthToken, User, -- cgit From 493d4d73ce427a686a674e00a2090d5aaec55a46 Mon Sep 17 00:00:00 2001 From: Ken Pepple Date: Thu, 10 Feb 2011 10:20:33 -0800 Subject: refactored api call to use instance_types --- nova/compute/api.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/nova/compute/api.py b/nova/compute/api.py index fc18765f6..9c83cfd16 100644 --- a/nova/compute/api.py +++ b/nova/compute/api.py @@ -90,8 +90,9 @@ class API(base.Base): other arguments check out ok.""" # FIXME(kpepple) this needs to be factored for api.py:2065 refactor - type_data = db.instance_type_get_by_name(context,\ - instance_type) + type_data = instance_types.get_instance_type(instance_type) + # type_data = db.instance_type_get_by_name(context,\ + # instance_type) num_instances = quota.allowed_instances(context, max_count, type_data) if num_instances < min_count: pid = context.project_id -- cgit From d601471f54de5db95cf06f4a558362f90cc65c6b Mon Sep 17 00:00:00 2001 From: Ken Pepple Date: Thu, 10 Feb 2011 10:28:52 -0800 Subject: typo --- nova/compute/api.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/nova/compute/api.py b/nova/compute/api.py index 9c83cfd16..5d6a42a6b 100644 --- a/nova/compute/api.py +++ b/nova/compute/api.py @@ -89,10 +89,7 @@ class API(base.Base): """Create the number of instances requested if quota and other arguments check out ok.""" - # FIXME(kpepple) this needs to be factored for api.py:2065 refactor type_data = instance_types.get_instance_type(instance_type) - # type_data = db.instance_type_get_by_name(context,\ - # instance_type) num_instances = quota.allowed_instances(context, max_count, type_data) if num_instances < min_count: pid = context.project_id -- cgit From 9029d89d26b9115cad282c6f3f9ee11c47a28444 Mon Sep 17 00:00:00 2001 From: Ken Pepple Date: Thu, 10 Feb 2011 11:19:02 -0800 Subject: flavorid needs to unique in model --- nova/db/sqlalchemy/models.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nova/db/sqlalchemy/models.py b/nova/db/sqlalchemy/models.py index 3f418392c..955d373fd 100644 --- a/nova/db/sqlalchemy/models.py +++ b/nova/db/sqlalchemy/models.py @@ -218,7 +218,7 @@ class InstanceTypes(BASE, NovaBase): memory_mb = Column(Integer) vcpus = Column(Integer) local_gb = Column(Integer) - flavorid = Column(Integer) + flavorid = Column(Integer, unique=True) class Volume(BASE, NovaBase): -- cgit From fd915e3db7f1006e67342b034eb8db0384c87d34 Mon Sep 17 00:00:00 2001 From: Ken Pepple Date: Thu, 10 Feb 2011 11:21:53 -0800 Subject: testing refactor --- bin/nova-manage | 2 +- nova/compute/instance_types.py | 6 +++--- nova/tests/test_instance_types.py | 10 ++++------ nova/tests/test_nova_manage.py | 32 ++++++++++++++++++++++---------- 4 files changed, 30 insertions(+), 20 deletions(-) diff --git a/bin/nova-manage b/bin/nova-manage index 3ee188593..e3c3e70f8 100755 --- a/bin/nova-manage +++ b/bin/nova-manage @@ -623,7 +623,7 @@ class InstanceTypeCommands(object): arguments: name memory_mb vcpus local_gb""" try: instance_types.create(name, memory, vcpus, local_gb, flavorid) - except exception.InvalidInputException, e: + except exception.InvalidInputException: print "Must supply valid parameters to create instance type" sys.exit(1) except exception.DBError, e: diff --git a/nova/compute/instance_types.py b/nova/compute/instance_types.py index b13ccda43..fcd4d8973 100644 --- a/nova/compute/instance_types.py +++ b/nova/compute/instance_types.py @@ -32,9 +32,9 @@ FLAGS = flags.FLAGS def create(name, memory, vcpus, local_gb, flavorid): """Creates instance types / flavors arguments: name memory_mb vcpus local_gb""" - for option in [memory, flavorid, vcpus]: - if option <= 0: - raise exception.InvalidInputException("Parameters incorrect") + if (memory <= 0) or (vcpus <= 0) or (local_gb < 0): + raise exception.InvalidInputException + db.instance_type_create(context.get_admin_context(), dict(name=name, memory_mb=memory, vcpus=vcpus, local_gb=local_gb, diff --git a/nova/tests/test_instance_types.py b/nova/tests/test_instance_types.py index 819431763..283f0bce8 100644 --- a/nova/tests/test_instance_types.py +++ b/nova/tests/test_instance_types.py @@ -14,7 +14,7 @@ """ Unit Tests for instance types code """ -import datetime +import time from nova import context from nova import db @@ -37,12 +37,10 @@ class InstanceTypeTestCase(test.TestCase): super(InstanceTypeTestCase, self).setUp() session = get_session() max_flavorid = session.query(models.InstanceTypes).\ - order_by("flavorid desc").first() + order_by("flavorid desc").\ + first() self.flavorid = max_flavorid["flavorid"] + 1 - self.name = str(datetime.datetime.utcnow()) - - def tearDown(self): - pass + self.name = str(int(time.time())) def test_instance_type_create_then_delete(self): """Ensure instance types can be created""" diff --git a/nova/tests/test_nova_manage.py b/nova/tests/test_nova_manage.py index a2fa919a0..d2c24e8b0 100644 --- a/nova/tests/test_nova_manage.py +++ b/nova/tests/test_nova_manage.py @@ -15,16 +15,25 @@ Tests For Nova-Manage """ +import time import os import subprocess from nova import test +from nova.db.sqlalchemy.session import get_session +from nova.db.sqlalchemy import models class NovaManageTestCase(test.TestCase): """Test case for nova-manage""" def setUp(self): super(NovaManageTestCase, self).setUp() + session = get_session() + max_flavorid = session.query(models.InstanceTypes).\ + order_by("flavorid desc").first() + self.flavorid = str(max_flavorid["flavorid"] + 1) + # self.flavorid = str(self.flavorid) + self.name = str(int(time.time())) def teardown(self): fnull.close() @@ -32,11 +41,11 @@ class NovaManageTestCase(test.TestCase): def test_create_and_delete_instance_types(self): fnull = open(os.devnull, 'w') retcode = subprocess.call(["bin/nova-manage", "instance_type", - "create", "test", "256", "1", - "120", "99"], stdout=fnull) + "create", self.name, "256", "1", + "120", self.flavorid], stdout=fnull) self.assertEqual(0, retcode) retcode = subprocess.call(["bin/nova-manage", "instance_type",\ - "delete", "test"], stdout=fnull) + "delete", self.name], stdout=fnull) self.assertEqual(0, retcode) def test_list_instance_types_or_flavors(self): @@ -52,17 +61,20 @@ class NovaManageTestCase(test.TestCase): "m1.medium"], stdout=fnull) self.assertEqual(0, retcode) - def test_should_raise_on_bad_create_args(self): + def test_should_error_on_bad_create_args(self): fnull = open(os.devnull, 'w') + # shouldn't be able to create instance type with 0 vcpus retcode = subprocess.call(["bin/nova-manage", "instance_type",\ - "create", "test", "256", "0",\ - "120", "99"], stdout=fnull) - self.assertEqual(1, retcode) + "create", self.name, "256", "0",\ + "120", self.flavorid], stdout=fnull) + # self.assertEqual(1, retcode, + # ("bin/nova-manage instance_type create %s 256 0 120 %s"\ + # % (self.name, self.flavorid))) def test_should_fail_on_duplicate_flavorid(self): fnull = open(os.devnull, 'w') retcode = subprocess.call(["bin/nova-manage", "instance_type",\ - "create", "test", "256", "1",\ + "create", self.name, "256", "1",\ "120", "1"], stdout=fnull) self.assertEqual(1, retcode) @@ -70,11 +82,11 @@ class NovaManageTestCase(test.TestCase): fnull = open(os.devnull, 'w') retcode = subprocess.call(["bin/nova-manage", "instance_type",\ "create", "fsfsfsdfsdf", "256", "1",\ - "120", "189"], stdout=fnull) + "120", self.flavorid], stdout=fnull) self.assertEqual(0, retcode) retcode = subprocess.call(["bin/nova-manage", "instance_type",\ "create", "fsfsfsdfsdf", "256", "1",\ - "120", "190"], stdout=fnull) + "120", self.flavorid], stdout=fnull) self.assertEqual(1, retcode) def test_instance_type_delete_should_fail_without_valid_name(self): -- cgit From d8a7a76cd4fd22a6ad9fc1a7b879a8dbffcede5f Mon Sep 17 00:00:00 2001 From: Cerberus Date: Thu, 10 Feb 2011 13:42:57 -0600 Subject: Some more cleanup --- nova/compute/manager.py | 7 ++++--- nova/virt/xenapi/vmops.py | 3 ++- nova/virt/xenapi_conn.py | 5 +---- plugins/xenserver/xenapi/etc/xapi.d/plugins/migration | 3 ++- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/nova/compute/manager.py b/nova/compute/manager.py index ac09f7c8c..54c3412f4 100644 --- a/nova/compute/manager.py +++ b/nova/compute/manager.py @@ -429,7 +429,8 @@ class ComputeManager(manager.Manager): self.db.migration_update(context, migration_id, { 'status': 'post-migrating', }) - # This is where we would update the VM record after resizing + #TODO(mdietz): This is where we would update the VM record + #after resizing service = self.db.service_get_by_host_and_topic(context, migration_ref['dest_host'], FLAGS.compute_topic) @@ -449,8 +450,8 @@ class ComputeManager(manager.Manager): migration_ref['instance_id']) # this may get passed into the following spawn instead - self.driver.attach_disk(context, instance_ref) - self.driver.spawn(context, instance_ref, preexisting=True) + disk_info = self.driver.attach_disk(context, instance_ref) + self.driver.spawn(context, instance_ref, disk_info=disk_info) self.db.migration_update(context, migration_id, {'status': 'finished', }) diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py index ea4b7899b..7d88876e4 100644 --- a/nova/virt/xenapi/vmops.py +++ b/nova/virt/xenapi/vmops.py @@ -106,7 +106,8 @@ class VMOps(object): instance.ramdisk_id, user, project, ImageType.KERNEL_RAMDISK) vm_ref = VMHelper.create_vm(self._session, instance, kernel, ramdisk, pv_kernel) - VMHelper.create_vbd(self._session, vm_ref, vdi_ref, 0, True) + VMHelper.create_vbd(session=self._session, vm_ref=vm_ref, vdi_ref=vdi_ref, + userdevice=0, bootable=True) if network_ref: VMHelper.create_vif(self._session, vm_ref, diff --git a/nova/virt/xenapi_conn.py b/nova/virt/xenapi_conn.py index cc43050b4..8c756a7e3 100644 --- a/nova/virt/xenapi_conn.py +++ b/nova/virt/xenapi_conn.py @@ -152,7 +152,7 @@ class XenAPIConnection(object): """List VM instances""" return self._vmops.list_instances() - def spawn(self, instance): + def spawn(self, instance, disk_info=None): """Create VM instance""" self._vmops.spawn(instance) @@ -164,9 +164,6 @@ class XenAPIConnection(object): """Resize a VM instance""" raise NotImplementedError() - def attach_disk(self, instance_ref): - - def reboot(self, instance): """Reboot VM instance""" self._vmops.reboot(instance) diff --git a/plugins/xenserver/xenapi/etc/xapi.d/plugins/migration b/plugins/xenserver/xenapi/etc/xapi.d/plugins/migration index e81b18a5e..0fb7b5806 100644 --- a/plugins/xenserver/xenapi/etc/xapi.d/plugins/migration +++ b/plugins/xenserver/xenapi/etc/xapi.d/plugins/migration @@ -70,6 +70,7 @@ def move_vhds_into_sr(session, args): instance_id = params['instance_id'] sr_path = get_sr_path(session) + sr_temp_path = "%s/images/" % sr_path # Discover the copied VHDs locally, and then set up paths to copy # them to under the SR @@ -77,7 +78,7 @@ def move_vhds_into_sr(session, args): source_base_copy_path = "%s/base_copy.vhd" % source_image_path source_cow_path = "%s/cow.vhd" % source_image_path - temp_vhd_path = "%s/instance%d/" % (sr_path, instance_id) + temp_vhd_path = "%s/instance%d/" % (sr_temp_path, instance_id) new_base_copy_path = "%s/%s.vhd" % (temp_vhd_path, str(uuid.uuid4())) new_cow_path = "%s/%s.vhd" % (temp_vhd_path, str(uuid.uuid4())) -- cgit From a6ce3b777221690df17137e70d6b7bf35ad10b02 Mon Sep 17 00:00:00 2001 From: Cerberus Date: Thu, 10 Feb 2011 13:59:54 -0600 Subject: Spawn from disk --- nova/compute/manager.py | 2 +- nova/virt/xenapi/vmops.py | 47 ++++++++++++++++++++++++++--------------------- nova/virt/xenapi_conn.py | 4 ++-- 3 files changed, 29 insertions(+), 24 deletions(-) diff --git a/nova/compute/manager.py b/nova/compute/manager.py index 54c3412f4..5f7d070af 100644 --- a/nova/compute/manager.py +++ b/nova/compute/manager.py @@ -451,7 +451,7 @@ class ComputeManager(manager.Manager): # this may get passed into the following spawn instead disk_info = self.driver.attach_disk(context, instance_ref) - self.driver.spawn(context, instance_ref, disk_info=disk_info) + self.driver.spawn(context, instance_ref, disk=disk_info) self.db.migration_update(context, migration_id, {'status': 'finished', }) diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py index 7d88876e4..ad46bb40d 100644 --- a/nova/virt/xenapi/vmops.py +++ b/nova/virt/xenapi/vmops.py @@ -69,7 +69,7 @@ class VMOps(object): LOG.debug(_("Starting instance %s"), instance.name) self._session.call_xenapi('VM.start', vm, False, False) - def spawn(self, instance): + def spawn(self, instance, disk): """Create VM instance""" vm = VMHelper.lookup(self._session, instance.name) if vm is not None: @@ -83,27 +83,32 @@ class VMOps(object): user = AuthManager().get_user(instance.user_id) project = AuthManager().get_project(instance.project_id) - #if kernel is not present we must download a raw disk - if instance.kernel_id: - disk_image_type = ImageType.DISK + + vdi_ref = kernel = ramdisk = pv_kernel = None + + # Are we building from a pre-existing disk? + if not disk: + #if kernel is not present we must download a raw disk + if instance.kernel_id: + disk_image_type = ImageType.DISK + else: + disk_image_type = ImageType.DISK_RAW + vdi_uuid = VMHelper.fetch_image(self._session, instance.id, + instance.image_id, user, project, disk_image_type) + vdi_ref = self._session.call_xenapi('VDI.get_by_uuid', vdi_uuid) + #Have a look at the VDI and see if it has a PV kernel + if not instance.kernel_id: + pv_kernel = VMHelper.lookup_image(self._session, instance.id, + vdi_ref) + if instance.kernel_id: + kernel = VMHelper.fetch_image(self._session, instance.id, + instance.kernel_id, user, project, ImageType.KERNEL_RAMDISK) + if instance.ramdisk_id: + ramdisk = VMHelper.fetch_image(self._session, instance.id, + instance.ramdisk_id, user, project, ImageType.KERNEL_RAMDISK) else: - disk_image_type = ImageType.DISK_RAW - vdi_uuid = VMHelper.fetch_image(self._session, instance.id, - instance.image_id, user, project, disk_image_type) - vdi_ref = self._session.call_xenapi('VDI.get_by_uuid', vdi_uuid) - #Have a look at the VDI and see if it has a PV kernel - pv_kernel = False - if not instance.kernel_id: - pv_kernel = VMHelper.lookup_image(self._session, instance.id, - vdi_ref) - kernel = None - if instance.kernel_id: - kernel = VMHelper.fetch_image(self._session, instance.id, - instance.kernel_id, user, project, ImageType.KERNEL_RAMDISK) - ramdisk = None - if instance.ramdisk_id: - ramdisk = VMHelper.fetch_image(self._session, instance.id, - instance.ramdisk_id, user, project, ImageType.KERNEL_RAMDISK) + vdi_ref = self._session.call_xenapi('VDI.get_by_uuid', disk) + vm_ref = VMHelper.create_vm(self._session, instance, kernel, ramdisk, pv_kernel) VMHelper.create_vbd(session=self._session, vm_ref=vm_ref, vdi_ref=vdi_ref, diff --git a/nova/virt/xenapi_conn.py b/nova/virt/xenapi_conn.py index 8c756a7e3..2fddb8c7f 100644 --- a/nova/virt/xenapi_conn.py +++ b/nova/virt/xenapi_conn.py @@ -152,9 +152,9 @@ class XenAPIConnection(object): """List VM instances""" return self._vmops.list_instances() - def spawn(self, instance, disk_info=None): + def spawn(self, instance, disk=None): """Create VM instance""" - self._vmops.spawn(instance) + self._vmops.spawn(instance, disk) def snapshot(self, instance, image_id): """ Create snapshot from a running VM instance """ -- cgit From a70ac6609713f2b610923a7ae382208f4d46b74a Mon Sep 17 00:00:00 2001 From: Cerberus Date: Thu, 10 Feb 2011 15:01:38 -0600 Subject: Typo fixes and some stupidity about the models --- nova/api/openstack/servers.py | 11 +++++------ nova/compute/manager.py | 2 +- nova/db/sqlalchemy/migrate_repo/versions/003_cactus.py | 4 ++++ nova/virt/xenapi/vmops.py | 2 +- 4 files changed, 11 insertions(+), 8 deletions(-) diff --git a/nova/api/openstack/servers.py b/nova/api/openstack/servers.py index 61dd3be36..06a40e92c 100644 --- a/nova/api/openstack/servers.py +++ b/nova/api/openstack/servers.py @@ -207,27 +207,26 @@ class Controller(wsgi.Controller): return faults.Fault(exc.HTTPNotImplemented()) def _action_confirm_resize(self, input_dict, req, id): - return fault.Fault(exc.HTTPNotImplemented()) + return faults.Fault(exc.HTTPNotImplemented()) def _action_revert_resize(self, input_dict, req, id): - return fault.Fault(exc.HTTPNotImplemented()) + return faults.Fault(exc.HTTPNotImplemented()) def _action_rebuild(self, input_dict, req, id): - return fault.Fault(exc.HTTPNotImplemented()) + return faults.Fault(exc.HTTPNotImplemented()) def _action_resize(self, input_dict, req, id): """ Resizes a given instance to the flavor size requested """ try: - resize_flavor = input_dict['resize']['flavorId'] + flavor_id = input_dict['resize']['flavorId'] self.compute_api.resize(req.environ['nova.context'], id, flavor_id) except: return faults.Fault(exc.HTTPUnprocessableEntity()) - return fault.Fault(exc.HTTPAccepted()) + return faults.Fault(exc.HTTPAccepted()) def _action_reboot(self, input_dict, req, id): - #TODO(sandy): rebuild/resize not supported. try: reboot_type = input_dict['reboot']['type'] except Exception: diff --git a/nova/compute/manager.py b/nova/compute/manager.py index 5f7d070af..9e8361563 100644 --- a/nova/compute/manager.py +++ b/nova/compute/manager.py @@ -401,7 +401,7 @@ class ComputeManager(manager.Manager): """Initiates the process of moving a running instance to another host, possibly changing the RAM and disk size in the process""" context = context.elevated() - instance_ref = self.db.instance_get(context. instance_id) + instance_ref = self.db.instance_get(context, instance_id) migration_ref = self.db.migration_create(context, { 'instance_id': instance_id, 'source_host': instance_ref['host'], diff --git a/nova/db/sqlalchemy/migrate_repo/versions/003_cactus.py b/nova/db/sqlalchemy/migrate_repo/versions/003_cactus.py index 4d01cd874..499465fce 100644 --- a/nova/db/sqlalchemy/migrate_repo/versions/003_cactus.py +++ b/nova/db/sqlalchemy/migrate_repo/versions/003_cactus.py @@ -27,6 +27,10 @@ meta = MetaData() # migrations = Table('migrations', meta, + Column('created_at', DateTime(timezone=False)), + Column('updated_at', DateTime(timezone=False)), + Column('deleted_at', DateTime(timezone=False)), + Column('deleted', Boolean(create_constraint=True, name=None)), Column('id', Integer(), primary_key=True, nullable=False), Column('source_host', String(255)), Column('dest_host', String(255)), diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py index ad46bb40d..3b14390b4 100644 --- a/nova/virt/xenapi/vmops.py +++ b/nova/virt/xenapi/vmops.py @@ -152,7 +152,7 @@ class VMOps(object): """ vm = None try: - if instance_or_vm.startswith("OpaqueRef:") + if instance_or_vm.startswith("OpaqueRef:"): # Got passed an opaque ref; return it return instance_or_vm else: -- cgit From 3fc68b805bb5326ef4fa2b8a51a58862ec23a6a4 Mon Sep 17 00:00:00 2001 From: Cerberus Date: Thu, 10 Feb 2011 15:04:06 -0600 Subject: Forgot the metadata includes --- nova/db/sqlalchemy/migrate_repo/versions/003_cactus.py | 1 + 1 file changed, 1 insertion(+) diff --git a/nova/db/sqlalchemy/migrate_repo/versions/003_cactus.py b/nova/db/sqlalchemy/migrate_repo/versions/003_cactus.py index 499465fce..38b711775 100644 --- a/nova/db/sqlalchemy/migrate_repo/versions/003_cactus.py +++ b/nova/db/sqlalchemy/migrate_repo/versions/003_cactus.py @@ -15,6 +15,7 @@ # License for the specific language governing permissions and limitations # under the License.from sqlalchemy import * +from sqlalchemy import * from migrate import * from nova import log as logging -- cgit From 68b7ae27036e1a9b16ceb835c5dc6b934e3b964a Mon Sep 17 00:00:00 2001 From: Cerberus Date: Thu, 10 Feb 2011 15:06:27 -0600 Subject: Forgot the metadata includes --- nova/db/sqlalchemy/migrate_repo/versions/003_cactus.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/nova/db/sqlalchemy/migrate_repo/versions/003_cactus.py b/nova/db/sqlalchemy/migrate_repo/versions/003_cactus.py index 38b711775..02d9177bd 100644 --- a/nova/db/sqlalchemy/migrate_repo/versions/003_cactus.py +++ b/nova/db/sqlalchemy/migrate_repo/versions/003_cactus.py @@ -23,6 +23,12 @@ from nova import log as logging meta = MetaData() +# Just for the ForeignKey and column creation to succeed, these are not the +# actual definitions of instances or services. +instances = Table('instances', meta, + Column('id', Integer(), primary_key=True, nullable=False), + ) + # # New Tables # -- cgit From 363371ddc6bbe008a536bda06da016385850a98a Mon Sep 17 00:00:00 2001 From: Cerberus Date: Thu, 10 Feb 2011 17:20:10 -0600 Subject: Forgot the metadata includes --- nova/db/api.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nova/db/api.py b/nova/db/api.py index 5da0e9840..03232385c 100644 --- a/nova/db/api.py +++ b/nova/db/api.py @@ -86,7 +86,7 @@ def service_get(context, service_id): def service_get_by_host_and_topic(context, host, topic): """Get a service by host it's on and topic it listens to""" - return IMPL.service_get(context, host, topic) + return IMPL.service_get_by_host_and_topic(context, host, topic) def service_get_all(context, disabled=False): -- cgit From 8ac02818a514716fa4899d633831877a388239c0 Mon Sep 17 00:00:00 2001 From: Ken Pepple Date: Thu, 10 Feb 2011 16:29:25 -0800 Subject: fixed destroy calls --- nova/compute/instance_types.py | 7 +------ nova/db/sqlalchemy/api.py | 14 +++++++------- nova/tests/test_instance_types.py | 3 +-- nova/tests/test_nova_manage.py | 10 +++++----- 4 files changed, 14 insertions(+), 20 deletions(-) diff --git a/nova/compute/instance_types.py b/nova/compute/instance_types.py index fcd4d8973..c6887795a 100644 --- a/nova/compute/instance_types.py +++ b/nova/compute/instance_types.py @@ -47,12 +47,7 @@ def destroy(name): if name == None: raise exception.InvalidInputException else: - records = db.instance_type_destroy(context.get_admin_context(), - name) - if records == 0: - raise exception.NotFound("Cannot find instance type named %s" % name) - else: - return records + db.instance_type_destroy(context.get_admin_context(), name) def get_all_types(inactive=0): diff --git a/nova/db/sqlalchemy/api.py b/nova/db/sqlalchemy/api.py index 1e13e4d2d..f8b0559d2 100644 --- a/nova/db/sqlalchemy/api.py +++ b/nova/db/sqlalchemy/api.py @@ -2086,10 +2086,10 @@ def instance_type_get_by_flavor_id(context, id): def instance_type_destroy(context, name): """ Marks specific instance_type as deleted""" session = get_session() - instance_type_ref = session.query(models.InstanceTypes).\ - filter_by(name=name) - rows = instance_type_ref.update(dict(deleted=1)) - if not rows: - raise exception.NotFound(_("Couldn't delete instance type %s") % name) - else: - return rows + try: + instance_type_ref = session.query(models.InstanceTypes).\ + filter_by(name=name) + instance_type_ref.update(dict(deleted=1)) + except: + raise exception.DBError + return instance_type_ref diff --git a/nova/tests/test_instance_types.py b/nova/tests/test_instance_types.py index 283f0bce8..36c29d336 100644 --- a/nova/tests/test_instance_types.py +++ b/nova/tests/test_instance_types.py @@ -50,8 +50,7 @@ class InstanceTypeTestCase(test.TestCase): self.assertNotEqual(len(starting_inst_list), len(new), 'instance was not created') - rows = instance_types.destroy(self.name) - self.assertEqual(rows, 1) + instance_types.destroy(self.name) self.assertEqual(1, instance_types.get_instance_type(self.name)["deleted"]) self.assertEqual(starting_inst_list, instance_types.get_all_types()) diff --git a/nova/tests/test_nova_manage.py b/nova/tests/test_nova_manage.py index d2c24e8b0..c1ef8cae9 100644 --- a/nova/tests/test_nova_manage.py +++ b/nova/tests/test_nova_manage.py @@ -89,8 +89,8 @@ class NovaManageTestCase(test.TestCase): "120", self.flavorid], stdout=fnull) self.assertEqual(1, retcode) - def test_instance_type_delete_should_fail_without_valid_name(self): - fnull = open(os.devnull, 'w') - retcode = subprocess.call(["bin/nova-manage", "instance_type",\ - "delete", "saefasff"], stdout=fnull) - self.assertEqual(1, retcode) + # def test_instance_type_delete_should_fail_without_valid_name(self): + # fnull = open(os.devnull, 'w') + # retcode = subprocess.call(["bin/nova-manage", "instance_type",\ + # "delete", "saefasff"], stdout=fnull) + # self.assertEqual(1, retcode) -- cgit From a36b67d192eb619963494896928efffef5dae4b6 Mon Sep 17 00:00:00 2001 From: Ken Pepple Date: Thu, 10 Feb 2011 18:35:10 -0800 Subject: after hours of tracking his prey, ken slowly crept behind the elusive wilderbeast test import hiding in the libvirt_conn.py bushes and gutted it with his steely blade --- nova/virt/libvirt_conn.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py index 14cc4cf39..35b78368e 100644 --- a/nova/virt/libvirt_conn.py +++ b/nova/virt/libvirt_conn.py @@ -55,7 +55,7 @@ from nova import db from nova import exception from nova import flags from nova import log as logging -from nova import test +#from nova import test from nova import utils #from nova.api import context from nova.auth import manager -- cgit From 42bd44db235ed2b2fb10e05d70de8d04b0fa869d Mon Sep 17 00:00:00 2001 From: Cerberus Date: Fri, 11 Feb 2011 11:14:51 -0600 Subject: First, not all --- nova/db/sqlalchemy/api.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nova/db/sqlalchemy/api.py b/nova/db/sqlalchemy/api.py index 98be39506..a6be844f8 100644 --- a/nova/db/sqlalchemy/api.py +++ b/nova/db/sqlalchemy/api.py @@ -164,7 +164,7 @@ def service_get_by_host_and_topic(context, host, topic): filter_by(disabled=False).\ filter_by(host=host).\ filter_by(topic=topic).\ - all() + first() @require_admin_context def service_get_all_by_host(context, host): -- cgit From 4a058908db774bfebce4ece814534225e123345c Mon Sep 17 00:00:00 2001 From: Josh Kearney Date: Fri, 11 Feb 2011 15:04:49 -0600 Subject: Added more columns to instance_types tables --- bin/nova-manage | 41 ++++++++++++++---- nova/compute/instance_types.py | 28 ++++++++++--- .../sqlalchemy/migrate_repo/versions/003_cactus.py | 4 +- nova/db/sqlalchemy/models.py | 3 ++ nova/tests/test_nova_manage.py | 48 ++++++++++++++++++---- 5 files changed, 101 insertions(+), 23 deletions(-) diff --git a/bin/nova-manage b/bin/nova-manage index e3c3e70f8..bd2fc0e81 100755 --- a/bin/nova-manage +++ b/bin/nova-manage @@ -614,15 +614,42 @@ class InstanceTypeCommands(object): """Class for managing instance types / flavors.""" def _print_instance_types(self, n, val): - print "%s: %s memory(MB), %s vcpus, %s storage(GB), %s flavorid"\ - % (n, val["memory_mb"], val["vcpus"], - val["local_gb"], val["flavorid"]) - - def create(self, name, memory, vcpus, local_gb, flavorid): + print ("%s: Memory: %sMB, VCPUS: %s, Storage: %sGB, FlavorID: %s, " + "Swap: %sGB, RXTX Quota: %sGB, RXTX Cap: %sMB") % ( + n, + val["memory_mb"], + val["vcpus"], + val["local_gb"], + val["flavorid"], + val["swap"], + val["rxtx_quota"], + val["rxtx_cap"]) + + def create( + self, + name, + memory, + vcpus, + local_gb, + flavorid, + swap, + rxtx_quota, + rxtx_cap): """Creates instance types / flavors - arguments: name memory_mb vcpus local_gb""" + arguments: name memory vcpus local_gb flavorid swap rxtx_quota + rxtx_cap + """ + try: - instance_types.create(name, memory, vcpus, local_gb, flavorid) + instance_types.create( + name, + memory, + vcpus, + local_gb, + flavorid, + swap, + rxtx_quota, + rxtx_cap) except exception.InvalidInputException: print "Must supply valid parameters to create instance type" sys.exit(1) diff --git a/nova/compute/instance_types.py b/nova/compute/instance_types.py index c6887795a..01abee584 100644 --- a/nova/compute/instance_types.py +++ b/nova/compute/instance_types.py @@ -29,16 +29,32 @@ from nova import exception FLAGS = flags.FLAGS -def create(name, memory, vcpus, local_gb, flavorid): +def create( + name, + memory, + vcpus, + local_gb, + flavorid, + swap=0, + rxtx_quota=0, + rxtx_cap=0): """Creates instance types / flavors - arguments: name memory_mb vcpus local_gb""" + arguments: name memory vcpus local_gb flavorid swap rxtx_quota rxtx_cap + """ if (memory <= 0) or (vcpus <= 0) or (local_gb < 0): raise exception.InvalidInputException - db.instance_type_create(context.get_admin_context(), - dict(name=name, memory_mb=memory, - vcpus=vcpus, local_gb=local_gb, - flavorid=flavorid)) + db.instance_type_create( + context.get_admin_context(), + dict( + name=name, + memory_mb=memory, + vcpus=vcpus, + local_gb=local_gb, + flavorid=flavorid, + swap=swap, + rxtx_quota=rxtx_quota, + rxtx_cap=rxtx_cap)) def destroy(name): diff --git a/nova/db/sqlalchemy/migrate_repo/versions/003_cactus.py b/nova/db/sqlalchemy/migrate_repo/versions/003_cactus.py index f95996042..fec191214 100644 --- a/nova/db/sqlalchemy/migrate_repo/versions/003_cactus.py +++ b/nova/db/sqlalchemy/migrate_repo/versions/003_cactus.py @@ -41,7 +41,9 @@ instance_types = Table('instance_types', meta, Column('vcpus', Integer(), nullable=False), Column('local_gb', Integer(), nullable=False), Column('flavorid', Integer(), nullable=False, unique=True), - ) + Column('swap', Integer(), nullable=False, default=0), + Column('rxtx_quota', Integer(), nullable=False, default=0), + Column('rxtx_cap', Integer(), nullable=False, default=0)) def upgrade(migrate_engine): diff --git a/nova/db/sqlalchemy/models.py b/nova/db/sqlalchemy/models.py index 955d373fd..8ee3e3532 100644 --- a/nova/db/sqlalchemy/models.py +++ b/nova/db/sqlalchemy/models.py @@ -219,6 +219,9 @@ class InstanceTypes(BASE, NovaBase): vcpus = Column(Integer) local_gb = Column(Integer) flavorid = Column(Integer, unique=True) + swap = Column(Integer, nullable=False, default=0) + rxtx_quota = Column(Integer, nullable=False, default=0) + rxtx_cap = Column(Integer, nullable=False, default=0) class Volume(BASE, NovaBase): diff --git a/nova/tests/test_nova_manage.py b/nova/tests/test_nova_manage.py index c1ef8cae9..5a6e1287f 100644 --- a/nova/tests/test_nova_manage.py +++ b/nova/tests/test_nova_manage.py @@ -40,9 +40,19 @@ class NovaManageTestCase(test.TestCase): def test_create_and_delete_instance_types(self): fnull = open(os.devnull, 'w') - retcode = subprocess.call(["bin/nova-manage", "instance_type", - "create", self.name, "256", "1", - "120", self.flavorid], stdout=fnull) + retcode = subprocess.call([ + "bin/nova-manage", + "instance_type", + "create", + self.name, + "256", + "1", + "120", + self.flavorid, + "2", + "10", + "10"], + stdout=fnull) self.assertEqual(0, retcode) retcode = subprocess.call(["bin/nova-manage", "instance_type",\ "delete", self.name], stdout=fnull) @@ -80,13 +90,33 @@ class NovaManageTestCase(test.TestCase): def test_should_fail_on_duplicate_name(self): fnull = open(os.devnull, 'w') - retcode = subprocess.call(["bin/nova-manage", "instance_type",\ - "create", "fsfsfsdfsdf", "256", "1",\ - "120", self.flavorid], stdout=fnull) + retcode = subprocess.call([ + "bin/nova-manage", + "instance_type", + "create", + "fsfsfsdfsdf", + "256", + "1", + "120", + self.flavorid, + "2", + "10", + "10"], + stdout=fnull) self.assertEqual(0, retcode) - retcode = subprocess.call(["bin/nova-manage", "instance_type",\ - "create", "fsfsfsdfsdf", "256", "1",\ - "120", self.flavorid], stdout=fnull) + retcode = subprocess.call([ + "bin/nova-manage", + "instance_type", + "create", + "fsfsfsdfsdf", + "256", + "1", + "120", + self.flavorid, + "2", + "10", + "10"], + stdout=fnull) self.assertEqual(1, retcode) # def test_instance_type_delete_should_fail_without_valid_name(self): -- cgit From e4061a0f5d06dfd6136c5dda94945214cc9a2cf5 Mon Sep 17 00:00:00 2001 From: Ken Pepple Date: Fri, 11 Feb 2011 13:11:28 -0800 Subject: more error checking on inputs and better errors returned --- nova/compute/instance_types.py | 38 ++++++++++++++++++++++++++------------ 1 file changed, 26 insertions(+), 12 deletions(-) diff --git a/nova/compute/instance_types.py b/nova/compute/instance_types.py index c6887795a..0cec4812e 100644 --- a/nova/compute/instance_types.py +++ b/nova/compute/instance_types.py @@ -32,22 +32,36 @@ FLAGS = flags.FLAGS def create(name, memory, vcpus, local_gb, flavorid): """Creates instance types / flavors arguments: name memory_mb vcpus local_gb""" - if (memory <= 0) or (vcpus <= 0) or (local_gb < 0): - raise exception.InvalidInputException - - db.instance_type_create(context.get_admin_context(), - dict(name=name, memory_mb=memory, - vcpus=vcpus, local_gb=local_gb, - flavorid=flavorid)) + for option in [memory, vcpus, local_gb, flavorid]: + try: + int(option) + except: + raise exception.InvalidInputException( + _("create arguments must be positive integers")) + if (int(memory) <= 0) or (int(vcpus) <= 0) or (int(local_gb) < 0): + raise exception.InvalidInputException( + _("create arguments must be positive integers")) + try: + db.instance_type_create(context.get_admin_context(), + dict(name=name, memory_mb=memory, + vcpus=vcpus, local_gb=local_gb, + flavorid=flavorid)) + except exception.DBError: + raise exception.ApiError(_("Cannot create instance type: %s"), + instance_type, "Invalid") def destroy(name): """Marks instance types / flavors as deleted arguments: name""" if name == None: - raise exception.InvalidInputException + raise exception.InvalidInputException(_("No instance type specified")) else: - db.instance_type_destroy(context.get_admin_context(), name) + try: + db.instance_type_destroy(context.get_admin_context(), name) + except exception.DBError: + raise exception.ApiError(_("Unknown instance type: %s"), + instance_type, "Invalid") def get_all_types(inactive=0): @@ -72,7 +86,7 @@ def get_instance_type(name): return inst_type except exception.DBError: raise exception.ApiError(_("Unknown instance type: %s"), - instance_type) + instance_type, "Invalid") def get_by_type(instance_type): @@ -85,7 +99,7 @@ def get_by_type(instance_type): return inst_type['name'] except exception.DBError: raise exception.ApiError(_("Unknown instance type: %s"), - instance_type) + instance_type, "Invalid") def get_by_flavor_id(flavor_id): @@ -98,4 +112,4 @@ def get_by_flavor_id(flavor_id): return flavor['name'] except exception.DBError: raise exception.ApiError(_("Unknown flavor: %s"), - flavor_id) + flavor_id, "Invalid") -- cgit From 40ec6d45a25bf997ae62dbbf08494aa39f047e33 Mon Sep 17 00:00:00 2001 From: Ken Pepple Date: Fri, 11 Feb 2011 13:53:54 -0800 Subject: updated tests and added more error checking --- bin/nova-manage | 15 ++++++--------- nova/compute/instance_types.py | 12 ++++++------ nova/db/sqlalchemy/api.py | 14 +++++++------- nova/tests/test_instance_types.py | 17 +++++++++++++++++ nova/tests/test_nova_manage.py | 21 +++++++++------------ 5 files changed, 45 insertions(+), 34 deletions(-) diff --git a/bin/nova-manage b/bin/nova-manage index e3c3e70f8..c819af628 100755 --- a/bin/nova-manage +++ b/bin/nova-manage @@ -623,15 +623,16 @@ class InstanceTypeCommands(object): arguments: name memory_mb vcpus local_gb""" try: instance_types.create(name, memory, vcpus, local_gb, flavorid) - except exception.InvalidInputException: + except exception.InvalidInputException, e: print "Must supply valid parameters to create instance type" + print e sys.exit(1) except exception.DBError, e: print "DB Error: %s" % e - sys.exit(1) + sys.exit(2) except: print "Unknown error" - sys.exit(1) + sys.exit(3) else: print "%s created" % name @@ -639,14 +640,10 @@ class InstanceTypeCommands(object): """Marks instance types / flavors as deleted arguments: name""" try: - records = instance_types.destroy(name) - except exception.InvalidParameters: + instance_types.destroy(name) + except exception.ApiError: print "Valid instance type name is required" sys.exit(1) - except exception.NotFound, e: - print "Instance type name %s not found. \ - No instance type deleted." % name - sys.exit(1) else: print "%s deleted" % name diff --git a/nova/compute/instance_types.py b/nova/compute/instance_types.py index 0cec4812e..f0b3fe473 100644 --- a/nova/compute/instance_types.py +++ b/nova/compute/instance_types.py @@ -48,7 +48,7 @@ def create(name, memory, vcpus, local_gb, flavorid): flavorid=flavorid)) except exception.DBError: raise exception.ApiError(_("Cannot create instance type: %s"), - instance_type, "Invalid") + name) def destroy(name): @@ -59,9 +59,9 @@ def destroy(name): else: try: db.instance_type_destroy(context.get_admin_context(), name) - except exception.DBError: + except exception.NotFound: raise exception.ApiError(_("Unknown instance type: %s"), - instance_type, "Invalid") + name) def get_all_types(inactive=0): @@ -86,7 +86,7 @@ def get_instance_type(name): return inst_type except exception.DBError: raise exception.ApiError(_("Unknown instance type: %s"), - instance_type, "Invalid") + name) def get_by_type(instance_type): @@ -99,7 +99,7 @@ def get_by_type(instance_type): return inst_type['name'] except exception.DBError: raise exception.ApiError(_("Unknown instance type: %s"), - instance_type, "Invalid") + instance_type) def get_by_flavor_id(flavor_id): @@ -112,4 +112,4 @@ def get_by_flavor_id(flavor_id): return flavor['name'] except exception.DBError: raise exception.ApiError(_("Unknown flavor: %s"), - flavor_id, "Invalid") + flavor_id) diff --git a/nova/db/sqlalchemy/api.py b/nova/db/sqlalchemy/api.py index f8b0559d2..323f9b965 100644 --- a/nova/db/sqlalchemy/api.py +++ b/nova/db/sqlalchemy/api.py @@ -2086,10 +2086,10 @@ def instance_type_get_by_flavor_id(context, id): def instance_type_destroy(context, name): """ Marks specific instance_type as deleted""" session = get_session() - try: - instance_type_ref = session.query(models.InstanceTypes).\ - filter_by(name=name) - instance_type_ref.update(dict(deleted=1)) - except: - raise exception.DBError - return instance_type_ref + instance_type_ref = session.query(models.InstanceTypes).\ + filter_by(name=name) + records = instance_type_ref.update(dict(deleted=1)) + if records == 0: + raise exception.NotFound + else: + return instance_type_ref diff --git a/nova/tests/test_instance_types.py b/nova/tests/test_instance_types.py index 36c29d336..68ca3b842 100644 --- a/nova/tests/test_instance_types.py +++ b/nova/tests/test_instance_types.py @@ -62,3 +62,20 @@ class InstanceTypeTestCase(test.TestCase): count() inst_types = instance_types.get_all_types() self.assertEqual(total_instance_types, len(inst_types)) + + def test_invalid_create_args_should_fail(self): + """Ensures that instance type creation fails with invalid args""" + self.assertRaises( + exception.InvalidInputException, + instance_types.create, self.name, 0, 1, 120, self.flavorid) + self.assertRaises( + exception.InvalidInputException, + instance_types.create, self.name, 256, -1, 120, self.flavorid) + self.assertRaises( + exception.InvalidInputException, + instance_types.create, self.name, 256, 1, "aa", self.flavorid) + + def test_non_existant_inst_type_shouldnt_delete(self): + """Ensures that instance type creation fails with invalid args""" + self.assertRaises(exception.ApiError, + instance_types.destroy, "sfsfsdfdfs") diff --git a/nova/tests/test_nova_manage.py b/nova/tests/test_nova_manage.py index c1ef8cae9..8ab23ba2b 100644 --- a/nova/tests/test_nova_manage.py +++ b/nova/tests/test_nova_manage.py @@ -32,7 +32,6 @@ class NovaManageTestCase(test.TestCase): max_flavorid = session.query(models.InstanceTypes).\ order_by("flavorid desc").first() self.flavorid = str(max_flavorid["flavorid"] + 1) - # self.flavorid = str(self.flavorid) self.name = str(int(time.time())) def teardown(self): @@ -42,7 +41,7 @@ class NovaManageTestCase(test.TestCase): fnull = open(os.devnull, 'w') retcode = subprocess.call(["bin/nova-manage", "instance_type", "create", self.name, "256", "1", - "120", self.flavorid], stdout=fnull) + "10", self.flavorid], stdout=fnull) self.assertEqual(0, retcode) retcode = subprocess.call(["bin/nova-manage", "instance_type",\ "delete", self.name], stdout=fnull) @@ -67,16 +66,14 @@ class NovaManageTestCase(test.TestCase): retcode = subprocess.call(["bin/nova-manage", "instance_type",\ "create", self.name, "256", "0",\ "120", self.flavorid], stdout=fnull) - # self.assertEqual(1, retcode, - # ("bin/nova-manage instance_type create %s 256 0 120 %s"\ - # % (self.name, self.flavorid))) + self.assertEqual(1, retcode) def test_should_fail_on_duplicate_flavorid(self): fnull = open(os.devnull, 'w') retcode = subprocess.call(["bin/nova-manage", "instance_type",\ "create", self.name, "256", "1",\ "120", "1"], stdout=fnull) - self.assertEqual(1, retcode) + self.assertEqual(3, retcode) def test_should_fail_on_duplicate_name(self): fnull = open(os.devnull, 'w') @@ -87,10 +84,10 @@ class NovaManageTestCase(test.TestCase): retcode = subprocess.call(["bin/nova-manage", "instance_type",\ "create", "fsfsfsdfsdf", "256", "1",\ "120", self.flavorid], stdout=fnull) - self.assertEqual(1, retcode) + self.assertEqual(3, retcode) - # def test_instance_type_delete_should_fail_without_valid_name(self): - # fnull = open(os.devnull, 'w') - # retcode = subprocess.call(["bin/nova-manage", "instance_type",\ - # "delete", "saefasff"], stdout=fnull) - # self.assertEqual(1, retcode) + def test_instance_type_delete_should_fail_without_valid_name(self): + fnull = open(os.devnull, 'w') + retcode = subprocess.call(["bin/nova-manage", "instance_type",\ + "delete", "doesntexist"], stdout=fnull) + self.assertEqual(1, retcode) -- cgit From b03d6f523a0dda7c942c298ac75bc46331085056 Mon Sep 17 00:00:00 2001 From: Ken Pepple Date: Fri, 11 Feb 2011 14:06:33 -0800 Subject: added instance_type_purge() to actually remove records from db --- nova/db/api.py | 7 +++++++ nova/db/sqlalchemy/api.py | 15 +++++++++++++++ nova/tests/test_instance_types.py | 1 + 3 files changed, 23 insertions(+) diff --git a/nova/db/api.py b/nova/db/api.py index 69f577764..4e53a8eee 100644 --- a/nova/db/api.py +++ b/nova/db/api.py @@ -1013,3 +1013,10 @@ def instance_type_get_by_flavor_id(context, id): def instance_type_destroy(context, name): """Delete a instance type""" return IMPL.instance_type_destroy(context, name) + + +def instance_type_purge(context, name): + """Purges (removes) an instance type from DB + Use instance_type_destroy for most cases + """ + return IMPL.instance_type_purge(context, name) diff --git a/nova/db/sqlalchemy/api.py b/nova/db/sqlalchemy/api.py index 323f9b965..a8ce22922 100644 --- a/nova/db/sqlalchemy/api.py +++ b/nova/db/sqlalchemy/api.py @@ -2093,3 +2093,18 @@ def instance_type_destroy(context, name): raise exception.NotFound else: return instance_type_ref + + +@require_admin_context +def instance_type_purge(context, name): + """ Removes specific instance_type from DB + Usually instance_type_destroy should be used + """ + session = get_session() + instance_type_ref = session.query(models.InstanceTypes).\ + filter_by(name=name) + records = instance_type_ref.delete() + if records == 0: + raise exception.NotFound + else: + return instance_type_ref diff --git a/nova/tests/test_instance_types.py b/nova/tests/test_instance_types.py index 68ca3b842..0d54cc283 100644 --- a/nova/tests/test_instance_types.py +++ b/nova/tests/test_instance_types.py @@ -54,6 +54,7 @@ class InstanceTypeTestCase(test.TestCase): self.assertEqual(1, instance_types.get_instance_type(self.name)["deleted"]) self.assertEqual(starting_inst_list, instance_types.get_all_types()) + db.instance_type_purge(context.get_admin_context(), self.name) def test_get_all_instance_types(self): """Ensures that all instance types can be retrieved""" -- cgit From f181051ac04084f2937438b61c988804fc2ef845 Mon Sep 17 00:00:00 2001 From: Cerberus Date: Fri, 11 Feb 2011 17:39:04 -0600 Subject: Cast to host --- nova/compute/manager.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/nova/compute/manager.py b/nova/compute/manager.py index 9e8361563..63632b538 100644 --- a/nova/compute/manager.py +++ b/nova/compute/manager.py @@ -411,7 +411,7 @@ class ComputeManager(manager.Manager): service = self.db.service_get_by_host_and_topic(context, instance_ref['host'], FLAGS.compute_topic) topic = self.db.queue_get_for(context, FLAGS.compute_topic, - service['id']) + service['host']) rpc.cast(context, topic, { 'method': 'resize_instance', 'migration_id': migration_ref['id'], }) @@ -435,7 +435,7 @@ class ComputeManager(manager.Manager): service = self.db.service_get_by_host_and_topic(context, migration_ref['dest_host'], FLAGS.compute_topic) topic = self.db.queue_get_for(context, FLAGS.compute_topic, - service['id']) + service['host']) rpc.cast(context, topic, { 'method': 'finish_resize', 'migration_id': migration_ref['id'], }) -- cgit From 66365ece306023c1cf848d452d5af2c418e4e14c Mon Sep 17 00:00:00 2001 From: Cerberus Date: Fri, 11 Feb 2011 18:04:00 -0600 Subject: More typos --- nova/compute/manager.py | 12 ++++++++++-- nova/db/sqlalchemy/api.py | 4 ++-- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/nova/compute/manager.py b/nova/compute/manager.py index 63632b538..9aa163b73 100644 --- a/nova/compute/manager.py +++ b/nova/compute/manager.py @@ -414,7 +414,11 @@ class ComputeManager(manager.Manager): service['host']) rpc.cast(context, topic, { 'method': 'resize_instance', - 'migration_id': migration_ref['id'], }) + 'args': { + 'migration_id': migration_ref['id'], + 'instance_id': instance_id, + }, + }) @exception.wrap_exception @checks_instance_lock @@ -438,7 +442,11 @@ class ComputeManager(manager.Manager): service['host']) rpc.cast(context, topic, { 'method': 'finish_resize', - 'migration_id': migration_ref['id'], }) + 'args': { + 'migration_id': migration_ref['id'], + 'instance_id': instance_id, + }, + }) @exception.wrap_exception @checks_instance_lock diff --git a/nova/db/sqlalchemy/api.py b/nova/db/sqlalchemy/api.py index a6be844f8..af343bc56 100644 --- a/nova/db/sqlalchemy/api.py +++ b/nova/db/sqlalchemy/api.py @@ -1941,7 +1941,7 @@ def migration_update(context, migration_id, values): @require_admin_context def migration_get(context, migration_id): session = get_session() - result = session.query(models.Migration.\ + result = session.query(models.Migration).\ filter_by(migration_id=migration_id)).first() if not result: raise exception.NotFound(_("No migration found with id %s") @@ -1952,7 +1952,7 @@ def migration_get(context, migration_id): @require_admin_context def migration_get_by_instance(context, instance_id): session = get_session() - result = session.query(models.Migration.\ + result = session.query(models.Migration).\ filter_by(instance_id=instance_id)).first() if not result: raise exception.NotFound(_("No migration found with instance id %s") -- cgit From 384a5aff50926784590ad66b92919b4d0408319d Mon Sep 17 00:00:00 2001 From: Cerberus Date: Fri, 11 Feb 2011 18:05:02 -0600 Subject: More typos --- nova/db/sqlalchemy/api.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/nova/db/sqlalchemy/api.py b/nova/db/sqlalchemy/api.py index af343bc56..6d52790a5 100644 --- a/nova/db/sqlalchemy/api.py +++ b/nova/db/sqlalchemy/api.py @@ -1942,7 +1942,7 @@ def migration_update(context, migration_id, values): def migration_get(context, migration_id): session = get_session() result = session.query(models.Migration).\ - filter_by(migration_id=migration_id)).first() + filter_by(migration_id=migration_id).first() if not result: raise exception.NotFound(_("No migration found with id %s") % migration_id) @@ -1953,7 +1953,7 @@ def migration_get(context, migration_id): def migration_get_by_instance(context, instance_id): session = get_session() result = session.query(models.Migration).\ - filter_by(instance_id=instance_id)).first() + filter_by(instance_id=instance_id).first() if not result: raise exception.NotFound(_("No migration found with instance id %s") % migration_id) -- cgit From f3b25fc06e3eff6f1b0e8fed4a0bf90612bf0230 Mon Sep 17 00:00:00 2001 From: Cerberus Date: Fri, 11 Feb 2011 18:07:34 -0600 Subject: More typos --- nova/compute/manager.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/nova/compute/manager.py b/nova/compute/manager.py index 9aa163b73..7c9e918fb 100644 --- a/nova/compute/manager.py +++ b/nova/compute/manager.py @@ -422,7 +422,7 @@ class ComputeManager(manager.Manager): @exception.wrap_exception @checks_instance_lock - def resize_instance(self, context, migration_id): + def resize_instance(self, context, instance_id, migration_id): """Starts the migration of a running instance to another host""" migration_ref = self.db.migration_get(context, migration_id) self.db.migration_update(context, migration_id, @@ -443,14 +443,14 @@ class ComputeManager(manager.Manager): rpc.cast(context, topic, { 'method': 'finish_resize', 'args': { - 'migration_id': migration_ref['id'], + 'migration_id': migration_id, 'instance_id': instance_id, }, }) @exception.wrap_exception @checks_instance_lock - def finish_resize(self, context, migration_id): + def finish_resize(self, context, instance_id, migration_id): """Completes the migration process by setting up the newly transferred disk and turning on the instance on its new host machine""" migration_ref = self.db.migration_get(context, migration_id) -- cgit From 520b1b50bc2b1d039ad2f89d791bba21b7a35f05 Mon Sep 17 00:00:00 2001 From: Cerberus Date: Fri, 11 Feb 2011 18:09:11 -0600 Subject: More typos --- nova/db/sqlalchemy/api.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/nova/db/sqlalchemy/api.py b/nova/db/sqlalchemy/api.py index 6d52790a5..8566bb91f 100644 --- a/nova/db/sqlalchemy/api.py +++ b/nova/db/sqlalchemy/api.py @@ -1930,19 +1930,19 @@ def migration_create(context, values): @require_admin_context -def migration_update(context, migration_id, values): +def migration_update(context, id, values): session = get_session() with session.begin(): - migration = migration_get(context, migration_id, session=session) + migration = migration_get(context, id, session=session) migration.update(values) return migration @require_admin_context -def migration_get(context, migration_id): +def migration_get(context, id): session = get_session() result = session.query(models.Migration).\ - filter_by(migration_id=migration_id).first() + filter_by(id=id).first() if not result: raise exception.NotFound(_("No migration found with id %s") % migration_id) -- cgit From 252ebfe9a039fb883e3e88eda8feafae037e750e Mon Sep 17 00:00:00 2001 From: Cerberus Date: Fri, 11 Feb 2011 18:12:18 -0600 Subject: More typos --- nova/db/api.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/nova/db/api.py b/nova/db/api.py index 03232385c..887f57885 100644 --- a/nova/db/api.py +++ b/nova/db/api.py @@ -259,6 +259,9 @@ def floating_ip_get_by_address(context, address): #################### +def migration_update(context, id, values): + """Update a migration instance""" + return IMPL.migration_update(context, id, values) def migration_create(context, values): """Create a migration record""" -- cgit From 875c4e1bab5e364a23695e46df69f1b21d9a8200 Mon Sep 17 00:00:00 2001 From: Cerberus Date: Mon, 14 Feb 2011 12:44:07 -0600 Subject: Derp --- nova/db/sqlalchemy/api.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nova/db/sqlalchemy/api.py b/nova/db/sqlalchemy/api.py index 8566bb91f..861d13716 100644 --- a/nova/db/sqlalchemy/api.py +++ b/nova/db/sqlalchemy/api.py @@ -1933,7 +1933,7 @@ def migration_create(context, values): def migration_update(context, id, values): session = get_session() with session.begin(): - migration = migration_get(context, id, session=session) + migration = migration_get(context, id) migration.update(values) return migration -- cgit From 4f23e417bb5ac3db8ac28dfb4b032a3e233c9821 Mon Sep 17 00:00:00 2001 From: Cerberus Date: Mon, 14 Feb 2011 12:50:54 -0600 Subject: More fixes --- nova/compute/manager.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/nova/compute/manager.py b/nova/compute/manager.py index 7c9e918fb..3fd37d831 100644 --- a/nova/compute/manager.py +++ b/nova/compute/manager.py @@ -425,10 +425,11 @@ class ComputeManager(manager.Manager): def resize_instance(self, context, instance_id, migration_id): """Starts the migration of a running instance to another host""" migration_ref = self.db.migration_get(context, migration_id) + instance_ref = self.db.instance_get(context, instance_id) self.db.migration_update(context, migration_id, { 'status': 'migrating', }) - self.driver.migrate_disk_and_power_off(context, instance, + self.driver.migrate_disk_and_power_off(instance_ref, migration_ref['dest_host']) self.db.migration_update(context, migration_id, -- cgit From 1631196f3f277608fb0569c7242a7d8391605d0d Mon Sep 17 00:00:00 2001 From: Cerberus Date: Mon, 14 Feb 2011 13:38:05 -0600 Subject: wharrgarbl --- nova/virt/xenapi/vmops.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py index 3b14390b4..d5b2c821c 100644 --- a/nova/virt/xenapi/vmops.py +++ b/nova/virt/xenapi/vmops.py @@ -230,7 +230,7 @@ class VMOps(object): label = "%s-snapshot" % instance.name try: - _, template_vdi_uuids = VMHelper.create_snapshot( + vdi_ref, template_vdi_uuids = VMHelper.create_snapshot( self._session, instance.id, vm_ref, label) return Snapshot(self, instance, template_vdi_uuids) except self.XenAPI.Failure, exc: @@ -262,22 +262,21 @@ class VMOps(object): # Now power down the instance and transfer the COW VHD self._shutdown(instance, method='clean') - _, vm_vdi_rec = get_vdi_for_vm_safely(session, vm_ref) + vdi_ref, vm_vdi_rec = get_vdi_for_vm_safely(session, vm_ref) params = {'host':dest, 'vdi_uuid': vm_vdi_rec['uuid'], 'dest_name': 'cow.vhd'} self._session.async_call_plugin('data_transfer', 'transfer_vhd', {'params': pickle.dumps(params)}) return snapshot.vdi_uuids[1], vm_vdi_rec['uuid'] - def attach_disk(self, instance): + def attach_disk(self, instance):hh vm_ref = VMHelper.lookup(self._session, instance.name) params = { 'instance_id': instance.id } self._session.async_call_plugin('migration', 'move_vhds_into_sr', {'params': pickle.dumps(params)}) - - _, vm_vdi_rec = get_vdi_for_vm_safely(session, vm_ref) + vdi_ref, vm_vdi_rec = get_vdi_for_vm_safely(session, vm_ref) VMHelper.scan_sr(self._session, instance.id, vm_vdi_rec['SR']) def resize(self, instance, flavor): -- cgit From 9f22390532332b955cb8d78ebfd8cf9670a63ac8 Mon Sep 17 00:00:00 2001 From: Cerberus Date: Mon, 14 Feb 2011 13:50:06 -0600 Subject: Snapshot correctly --- nova/virt/xenapi/vmops.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py index d5b2c821c..fc5cd84e1 100644 --- a/nova/virt/xenapi/vmops.py +++ b/nova/virt/xenapi/vmops.py @@ -211,10 +211,11 @@ class VMOps(object): def _get_snapshot(self, instance): class Snapshot(object): - def __init__(self, virt, instance, vdis): + def __init__(self, virt, instance, vm_ref, vdis): self.instance = instance self.vdi_uuids = vdis self.virt = virt + self.vm_ref = vm_ref def __enter__(self): return self @@ -230,9 +231,10 @@ class VMOps(object): label = "%s-snapshot" % instance.name try: - vdi_ref, template_vdi_uuids = VMHelper.create_snapshot( + template_vm_ref, template_vdi_uuids = VMHelper.create_snapshot( self._session, instance.id, vm_ref, label) - return Snapshot(self, instance, template_vdi_uuids) + return Snapshot(self, instance, template_vm_ref, + template_vdi_uuids) except self.XenAPI.Failure, exc: logging.error(_("Unable to Snapshot %(vm_ref)s: %(exc)s") % locals()) @@ -269,7 +271,7 @@ class VMOps(object): {'params': pickle.dumps(params)}) return snapshot.vdi_uuids[1], vm_vdi_rec['uuid'] - def attach_disk(self, instance):hh + def attach_disk(self, instance): vm_ref = VMHelper.lookup(self._session, instance.name) params = { 'instance_id': instance.id } -- cgit From fc8394a80b28f94561aa9ebf94c067ce2d1efd3b Mon Sep 17 00:00:00 2001 From: Cerberus Date: Mon, 14 Feb 2011 14:25:00 -0600 Subject: Snapshot correctly --- nova/virt/xenapi/vmops.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py index fc5cd84e1..a327f1d36 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 pickle import subprocess import tempfile import uuid @@ -262,7 +263,7 @@ class VMOps(object): {'params': pickle.dumps(params)}) # Now power down the instance and transfer the COW VHD - self._shutdown(instance, method='clean') + self._shutdown(instance, snapshot.vm_ref, method='clean') vdi_ref, vm_vdi_rec = get_vdi_for_vm_safely(session, vm_ref) params = {'host':dest, 'vdi_uuid': vm_vdi_rec['uuid'], -- cgit From 411d828fc3511a09420e579ceee65a9470242509 Mon Sep 17 00:00:00 2001 From: Cerberus Date: Mon, 14 Feb 2011 14:40:58 -0600 Subject: hurr --- nova/virt/xenapi/vm_utils.py | 38 +++++++++++++++++++++----------------- nova/virt/xenapi/vmops.py | 8 +++++--- 2 files changed, 26 insertions(+), 20 deletions(-) diff --git a/nova/virt/xenapi/vm_utils.py b/nova/virt/xenapi/vm_utils.py index e16662aad..2c4ae6aa2 100644 --- a/nova/virt/xenapi/vm_utils.py +++ b/nova/virt/xenapi/vm_utils.py @@ -238,6 +238,24 @@ class VMHelper(HelperBase): % locals()) return vdi_ref + @classmethod + def get_vdi_for_vm_safely(cls, session, vm_ref): + vdi_refs = VMHelper.lookup_vm_vdis(session, vm_ref) + if vdi_refs is None: + raise Exception(_("No VDIs found for VM %s") % vm_ref) + else: + num_vdis = len(vdi_refs) + if num_vdis != 1: + raise Exception(_("Unexpected number of VDIs (%(num_vdis)s) found" + " for VM %(vm_ref)s") % locals()) + + vdi_ref = vdi_refs[0] + vdi_rec = session.get_xenapi().VDI.get_record(vdi_ref) + return vdi_ref, vdi_rec + + + + @classmethod def create_snapshot(cls, session, instance_id, vm_ref, label): """ Creates Snapshot (Template) VM, Snapshot VBD, Snapshot VDI, @@ -248,7 +266,7 @@ class VMHelper(HelperBase): LOG.debug(_("Snapshotting VM %(vm_ref)s with label '%(label)s'...") % locals()) - vm_vdi_ref, vm_vdi_rec = get_vdi_for_vm_safely(session, vm_ref) + vm_vdi_ref, vm_vdi_rec = self.get_vdi_for_vm_safely(session, vm_ref) vm_vdi_uuid = vm_vdi_rec["uuid"] sr_ref = vm_vdi_rec["SR"] @@ -256,7 +274,8 @@ class VMHelper(HelperBase): task = session.call_xenapi('Async.VM.snapshot', vm_ref, label) template_vm_ref = session.wait_for_task(instance_id, task) - template_vdi_rec = get_vdi_for_vm_safely(session, template_vm_ref)[1] + template_vdi_rec = self.get_vdi_for_vm_safely(session, + template_vm_ref)[1] template_vdi_uuid = template_vdi_rec["uuid"] LOG.debug(_('Created snapshot %(template_vm_ref)s from' @@ -568,21 +587,6 @@ def wait_for_vhd_coalesce(session, instance_id, sr_ref, vdi_ref, return parent_uuid -def get_vdi_for_vm_safely(session, vm_ref): - vdi_refs = VMHelper.lookup_vm_vdis(session, vm_ref) - if vdi_refs is None: - raise Exception(_("No VDIs found for VM %s") % vm_ref) - else: - num_vdis = len(vdi_refs) - if num_vdis != 1: - raise Exception(_("Unexpected number of VDIs (%(num_vdis)s) found" - " for VM %(vm_ref)s") % locals()) - - vdi_ref = vdi_refs[0] - vdi_rec = session.get_xenapi().VDI.get_record(vdi_ref) - return vdi_ref, vdi_rec - - def find_sr(session): host = session.get_xenapi_host() srs = session.get_xenapi().SR.get_all() diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py index a327f1d36..6a7308c74 100644 --- a/nova/virt/xenapi/vmops.py +++ b/nova/virt/xenapi/vmops.py @@ -263,9 +263,10 @@ class VMOps(object): {'params': pickle.dumps(params)}) # Now power down the instance and transfer the COW VHD - self._shutdown(instance, snapshot.vm_ref, method='clean') + self._shutdown(instance, vm_ref, method='clean') - vdi_ref, vm_vdi_rec = get_vdi_for_vm_safely(session, vm_ref) + vdi_ref, vm_vdi_rec = \ + VMHelper.get_vdi_for_vm_safely(session, vm_ref) params = {'host':dest, 'vdi_uuid': vm_vdi_rec['uuid'], 'dest_name': 'cow.vhd'} self._session.async_call_plugin('data_transfer', 'transfer_vhd', @@ -279,7 +280,8 @@ class VMOps(object): self._session.async_call_plugin('migration', 'move_vhds_into_sr', {'params': pickle.dumps(params)}) - vdi_ref, vm_vdi_rec = get_vdi_for_vm_safely(session, vm_ref) + vdi_ref, vm_vdi_rec = \ + VMHelper.get_vdi_for_vm_safely(session, vm_ref) VMHelper.scan_sr(self._session, instance.id, vm_vdi_rec['SR']) def resize(self, instance, flavor): -- cgit From 7bb6122549ad5ac549465f0012020f8e5dc9d506 Mon Sep 17 00:00:00 2001 From: Cerberus Date: Mon, 14 Feb 2011 15:26:08 -0600 Subject: Some refactoring --- nova/compute/manager.py | 4 ++-- nova/virt/xenapi/vm_utils.py | 12 ++++++++---- nova/virt/xenapi/vmops.py | 13 ++++++++----- nova/virt/xenapi_conn.py | 2 +- plugins/xenserver/xenapi/etc/xapi.d/plugins/migration | 8 ++++++-- 5 files changed, 25 insertions(+), 14 deletions(-) diff --git a/nova/compute/manager.py b/nova/compute/manager.py index 3fd37d831..0b0966324 100644 --- a/nova/compute/manager.py +++ b/nova/compute/manager.py @@ -405,7 +405,7 @@ class ComputeManager(manager.Manager): migration_ref = self.db.migration_create(context, { 'instance_id': instance_id, 'source_host': instance_ref['host'], - 'dest_host': socket.gethostbyname(socket.gethostname()), + 'dest_host': socket.gethostname(), 'status': 'pre-migrating' }) LOG.audit(_('instance %s: migrating to '), instance_id, context=context) service = self.db.service_get_by_host_and_topic(context, @@ -459,7 +459,7 @@ class ComputeManager(manager.Manager): migration_ref['instance_id']) # this may get passed into the following spawn instead - disk_info = self.driver.attach_disk(context, instance_ref) + disk_info = self.driver.attach_disk(instance_ref) self.driver.spawn(context, instance_ref, disk=disk_info) self.db.migration_update(context, migration_id, diff --git a/nova/virt/xenapi/vm_utils.py b/nova/virt/xenapi/vm_utils.py index 2c4ae6aa2..eeb5502ed 100644 --- a/nova/virt/xenapi/vm_utils.py +++ b/nova/virt/xenapi/vm_utils.py @@ -266,7 +266,7 @@ class VMHelper(HelperBase): LOG.debug(_("Snapshotting VM %(vm_ref)s with label '%(label)s'...") % locals()) - vm_vdi_ref, vm_vdi_rec = self.get_vdi_for_vm_safely(session, vm_ref) + vm_vdi_ref, vm_vdi_rec = cls.get_vdi_for_vm_safely(session, vm_ref) vm_vdi_uuid = vm_vdi_rec["uuid"] sr_ref = vm_vdi_rec["SR"] @@ -274,7 +274,7 @@ class VMHelper(HelperBase): task = session.call_xenapi('Async.VM.snapshot', vm_ref, label) template_vm_ref = session.wait_for_task(instance_id, task) - template_vdi_rec = self.get_vdi_for_vm_safely(session, + template_vdi_rec = cls.get_vdi_for_vm_safely(session, template_vm_ref)[1] template_vdi_uuid = template_vdi_rec["uuid"] @@ -287,6 +287,12 @@ class VMHelper(HelperBase): #TODO(sirp): we need to assert only one parent, not parents two deep return template_vm_ref, [template_vdi_uuid, parent_uuid] + @classmethod + def get_sr(cls, session, sr_label='slices'): + """ Finds the SR named by the given name label and returns + the UUID """ + return session.call_xenapi('SR.get_by_name_label', sr_label)[0] + @classmethod def upload_image(cls, session, instance_id, vdi_uuids, image_id): """ Requests that the Glance plugin bundle the specified VDIs and @@ -504,8 +510,6 @@ class VMHelper(HelperBase): session.wait_for_task(instance_id, task) - - def get_rrd(host, uuid): """Return the VM RRD XML as a string""" try: diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py index 6a7308c74..470b6ea8c 100644 --- a/nova/virt/xenapi/vmops.py +++ b/nova/virt/xenapi/vmops.py @@ -266,7 +266,7 @@ class VMOps(object): self._shutdown(instance, vm_ref, method='clean') vdi_ref, vm_vdi_rec = \ - VMHelper.get_vdi_for_vm_safely(session, vm_ref) + VMHelper.get_vdi_for_vm_safely(self._session, vm_ref) params = {'host':dest, 'vdi_uuid': vm_vdi_rec['uuid'], 'dest_name': 'cow.vhd'} self._session.async_call_plugin('data_transfer', 'transfer_vhd', @@ -277,12 +277,15 @@ class VMOps(object): vm_ref = VMHelper.lookup(self._session, instance.name) params = { 'instance_id': instance.id } - self._session.async_call_plugin('migration', 'move_vhds_into_sr', + new_base_copy_uuid, new_cow_uuid = self._session.async_call_plugin( + 'migration', 'move_vhds_into_sr', {'params': pickle.dumps(params)}) - vdi_ref, vm_vdi_rec = \ - VMHelper.get_vdi_for_vm_safely(session, vm_ref) - VMHelper.scan_sr(self._session, instance.id, vm_vdi_rec['SR']) + # Now we rescan the SR so we find the VHDs + sr_ref = VMHelper.get_sr(self._session) + VMHelper.scan_sr(self._session, instance.id, sr_ref) + + return new_base_copy_uuid def resize(self, instance, flavor): """Resize a running instance by changing it's RAM and disk size """ diff --git a/nova/virt/xenapi_conn.py b/nova/virt/xenapi_conn.py index 2fddb8c7f..6869ce8d8 100644 --- a/nova/virt/xenapi_conn.py +++ b/nova/virt/xenapi_conn.py @@ -199,7 +199,7 @@ class XenAPIConnection(object): def attach_disk(self, instance): """Moves the copied VDIs into the SR""" - self._vmops.attach_disk(instance) + return self._vmops.attach_disk(instance) def suspend(self, instance, callback): """suspend the specified instance""" diff --git a/plugins/xenserver/xenapi/etc/xapi.d/plugins/migration b/plugins/xenserver/xenapi/etc/xapi.d/plugins/migration index 0fb7b5806..71d4473c5 100644 --- a/plugins/xenserver/xenapi/etc/xapi.d/plugins/migration +++ b/plugins/xenserver/xenapi/etc/xapi.d/plugins/migration @@ -78,9 +78,12 @@ def move_vhds_into_sr(session, args): source_base_copy_path = "%s/base_copy.vhd" % source_image_path source_cow_path = "%s/cow.vhd" % source_image_path + new_base_copy_uuid = str(uuid.uuid4()) + new_cow_uuid = str(uuid.uuid4()) + temp_vhd_path = "%s/instance%d/" % (sr_temp_path, instance_id) - new_base_copy_path = "%s/%s.vhd" % (temp_vhd_path, str(uuid.uuid4())) - new_cow_path = "%s/%s.vhd" % (temp_vhd_path, str(uuid.uuid4())) + new_base_copy_path = "%s/%s.vhd" % (temp_vhd_path, new_base_copy_uuid) + new_cow_path = "%s/%s.vhd" % (temp_vhd_path, new_cow_uuid) os.mkdir(temp_vhd_path) shutil.move(source_base_copy_path, new_base_copy_path) @@ -94,6 +97,7 @@ def move_vhds_into_sr(session, args): shutil.move("%s/*.vhd" % temp_vhd_path, sr_path) os.rmdir(temp_vhd_path) + return (new_base_copy_uuid, new_cow_uuid) def transfer_vhd(session, args): -- cgit From fad5baf307b74a92fd5b9d8e2d1479f558e180aa Mon Sep 17 00:00:00 2001 From: Cerberus Date: Mon, 14 Feb 2011 15:55:52 -0600 Subject: hurr --- nova/virt/xenapi/vm_utils.py | 12 ++++++++---- nova/virt/xenapi/vmops.py | 13 ++++++++----- plugins/xenserver/xenapi/etc/xapi.d/plugins/migration | 7 ++++--- 3 files changed, 20 insertions(+), 12 deletions(-) diff --git a/nova/virt/xenapi/vm_utils.py b/nova/virt/xenapi/vm_utils.py index eeb5502ed..23f9547d7 100644 --- a/nova/virt/xenapi/vm_utils.py +++ b/nova/virt/xenapi/vm_utils.py @@ -504,10 +504,14 @@ class VMHelper(HelperBase): return {"Unable to retrieve diagnostics": e} @classmethod - def scan_sr(cls, session, instance_id, sr_ref): - LOG.debug(_("Re-scanning SR %s"), sr_ref) - task = session.call_xenapi('Async.SR.scan', sr_ref) - session.wait_for_task(instance_id, task) + def scan_sr(cls, session, instance_id=None, sr_ref=None): + if sr_ref: + LOG.debug(_("Re-scanning SR %s"), sr_ref) + task = session.call_xenapi('Async.SR.scan', sr_ref) + session.wait_for_task(instance_id, task) + else: + sr_ref = cls.get_sr(session) + session.call_xen_api('SR.scan', sr_ref) def get_rrd(host, uuid): diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py index 470b6ea8c..17d42d542 100644 --- a/nova/virt/xenapi/vmops.py +++ b/nova/virt/xenapi/vmops.py @@ -276,14 +276,17 @@ class VMOps(object): def attach_disk(self, instance): vm_ref = VMHelper.lookup(self._session, instance.name) - params = { 'instance_id': instance.id } - new_base_copy_uuid, new_cow_uuid = self._session.async_call_plugin( - 'migration', 'move_vhds_into_sr', + new_base_copy_uuid = str(uuid.uuid4()) + + params = { 'instance_id': instance.id, + 'new_base_copy_uuid': new_base_copy_uuid, + 'new_cow_uuid': str(uuid.uuid4() } + + self._session.async_call_plugin('migration', 'move_vhds_into_sr', {'params': pickle.dumps(params)}) # Now we rescan the SR so we find the VHDs - sr_ref = VMHelper.get_sr(self._session) - VMHelper.scan_sr(self._session, instance.id, sr_ref) + VMHelper.scan_sr(self._session) return new_base_copy_uuid diff --git a/plugins/xenserver/xenapi/etc/xapi.d/plugins/migration b/plugins/xenserver/xenapi/etc/xapi.d/plugins/migration index 71d4473c5..e73480445 100644 --- a/plugins/xenserver/xenapi/etc/xapi.d/plugins/migration +++ b/plugins/xenserver/xenapi/etc/xapi.d/plugins/migration @@ -69,6 +69,9 @@ def move_vhds_into_sr(session, args): params = pickle.dumps(args) instance_id = params['instance_id'] + new_base_copy_uuid = params['new_base_copy_uuid'] + new_cow_uuid = params['new_cow_uuid'] + sr_path = get_sr_path(session) sr_temp_path = "%s/images/" % sr_path @@ -78,8 +81,6 @@ def move_vhds_into_sr(session, args): source_base_copy_path = "%s/base_copy.vhd" % source_image_path source_cow_path = "%s/cow.vhd" % source_image_path - new_base_copy_uuid = str(uuid.uuid4()) - new_cow_uuid = str(uuid.uuid4()) temp_vhd_path = "%s/instance%d/" % (sr_temp_path, instance_id) new_base_copy_path = "%s/%s.vhd" % (temp_vhd_path, new_base_copy_uuid) @@ -97,7 +98,7 @@ def move_vhds_into_sr(session, args): shutil.move("%s/*.vhd" % temp_vhd_path, sr_path) os.rmdir(temp_vhd_path) - return (new_base_copy_uuid, new_cow_uuid) + return None def transfer_vhd(session, args): -- cgit From 9a71c79dc3beb554c86a1b1b5d03ab66c6e96edc Mon Sep 17 00:00:00 2001 From: Cerberus Date: Mon, 14 Feb 2011 16:24:51 -0600 Subject: Typo fixes --- nova/compute/manager.py | 2 +- nova/virt/xenapi/vm_utils.py | 2 +- nova/virt/xenapi/vmops.py | 2 +- plugins/xenserver/xenapi/etc/xapi.d/plugins/migration | 3 +-- 4 files changed, 4 insertions(+), 5 deletions(-) diff --git a/nova/compute/manager.py b/nova/compute/manager.py index 0b0966324..23d2b80ac 100644 --- a/nova/compute/manager.py +++ b/nova/compute/manager.py @@ -460,7 +460,7 @@ class ComputeManager(manager.Manager): # this may get passed into the following spawn instead disk_info = self.driver.attach_disk(instance_ref) - self.driver.spawn(context, instance_ref, disk=disk_info) + self.driver.spawn(instance_ref, disk=disk_info) self.db.migration_update(context, migration_id, {'status': 'finished', }) diff --git a/nova/virt/xenapi/vm_utils.py b/nova/virt/xenapi/vm_utils.py index 23f9547d7..08064b786 100644 --- a/nova/virt/xenapi/vm_utils.py +++ b/nova/virt/xenapi/vm_utils.py @@ -511,7 +511,7 @@ class VMHelper(HelperBase): session.wait_for_task(instance_id, task) else: sr_ref = cls.get_sr(session) - session.call_xen_api('SR.scan', sr_ref) + session.call_xenapi('SR.scan', sr_ref) def get_rrd(host, uuid): diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py index 17d42d542..6c6d04dbf 100644 --- a/nova/virt/xenapi/vmops.py +++ b/nova/virt/xenapi/vmops.py @@ -280,7 +280,7 @@ class VMOps(object): params = { 'instance_id': instance.id, 'new_base_copy_uuid': new_base_copy_uuid, - 'new_cow_uuid': str(uuid.uuid4() } + 'new_cow_uuid': str(uuid.uuid4()) } self._session.async_call_plugin('migration', 'move_vhds_into_sr', {'params': pickle.dumps(params)}) diff --git a/plugins/xenserver/xenapi/etc/xapi.d/plugins/migration b/plugins/xenserver/xenapi/etc/xapi.d/plugins/migration index e73480445..cf7f378fa 100644 --- a/plugins/xenserver/xenapi/etc/xapi.d/plugins/migration +++ b/plugins/xenserver/xenapi/etc/xapi.d/plugins/migration @@ -25,7 +25,6 @@ import os.path import pickle import shutil import subprocess -import uuid import XenAPIPlugin @@ -114,7 +113,7 @@ def transfer_vhd(session, args): source_path = "%s/%s" % (sr_path, vhd_path) dest_path = '%s:%sinstance%d/%s' % (host, IMAGE_PATH, instance_id, dest_name) - rsync_args = [['nohup', RSYNC, '-av', '--progress', + rsync_args = ['nohup', RSYNC, '-av', '--progress', '-e "ssh -o StrictHostKeyChecking=no"', source_path, dest_path] if subprocess.call(rsync_args) != 0: -- cgit From 0bd48e3d53c6fce04b0c5e483537b3fd31c7364a Mon Sep 17 00:00:00 2001 From: Cerberus Date: Mon, 14 Feb 2011 17:24:33 -0600 Subject: bad plugin --- nova/virt/xenapi/vmops.py | 2 +- plugins/xenserver/xenapi/etc/xapi.d/plugins/migration | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py index 6c6d04dbf..5ab73d562 100644 --- a/nova/virt/xenapi/vmops.py +++ b/nova/virt/xenapi/vmops.py @@ -269,7 +269,7 @@ class VMOps(object): VMHelper.get_vdi_for_vm_safely(self._session, vm_ref) params = {'host':dest, 'vdi_uuid': vm_vdi_rec['uuid'], 'dest_name': 'cow.vhd'} - self._session.async_call_plugin('data_transfer', 'transfer_vhd', + self._session.async_call_plugin('migration', 'transfer_vhd', {'params': pickle.dumps(params)}) return snapshot.vdi_uuids[1], vm_vdi_rec['uuid'] diff --git a/plugins/xenserver/xenapi/etc/xapi.d/plugins/migration b/plugins/xenserver/xenapi/etc/xapi.d/plugins/migration index cf7f378fa..c68fc93c5 100644 --- a/plugins/xenserver/xenapi/etc/xapi.d/plugins/migration +++ b/plugins/xenserver/xenapi/etc/xapi.d/plugins/migration @@ -120,4 +120,5 @@ def transfer_vhd(session, args): raise Exception("Unexpected VHD transfer failure") if __name__ == '__main__': - XenAPIPlugin.dispatch({'transfer_vhd': transfer_vhd}) + XenAPIPlugin.dispatch({'transfer_vhd': transfer_vhd, + 'move_vhd_into_sr':move_vhd_into_sr, }) -- cgit From e44a91ced3d19a3bca10457239592307bf6f829b Mon Sep 17 00:00:00 2001 From: Cerberus Date: Mon, 14 Feb 2011 17:31:20 -0600 Subject: bad plugin --- plugins/xenserver/xenapi/etc/xapi.d/plugins/migration | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/plugins/xenserver/xenapi/etc/xapi.d/plugins/migration b/plugins/xenserver/xenapi/etc/xapi.d/plugins/migration index c68fc93c5..c1f5b7528 100644 --- a/plugins/xenserver/xenapi/etc/xapi.d/plugins/migration +++ b/plugins/xenserver/xenapi/etc/xapi.d/plugins/migration @@ -65,7 +65,7 @@ def find_sr(session): def move_vhds_into_sr(session, args): """Moves the VHDs from their copied location to the SR""" - params = pickle.dumps(args) + params = pickle.loads(exists(args, 'params')) instance_id = params['instance_id'] new_base_copy_uuid = params['new_base_copy_uuid'] @@ -102,7 +102,7 @@ def move_vhds_into_sr(session, args): def transfer_vhd(session, args): """Rsyncs a VHD to an adjacent host""" - params = pickle.dumps(args) + params = pickle.loads(exists(args, 'params')) instance_id = params['instance_id'] host = params['host'] vdi_uuid = params['vdi_uuid'] @@ -121,4 +121,4 @@ def transfer_vhd(session, args): if __name__ == '__main__': XenAPIPlugin.dispatch({'transfer_vhd': transfer_vhd, - 'move_vhd_into_sr':move_vhd_into_sr, }) + 'move_vhds_into_sr':move_vhds_into_sr, }) -- cgit From b7cf8f233a585043f0aa85f4d26dc2fb5a6701c7 Mon Sep 17 00:00:00 2001 From: Cerberus Date: Mon, 14 Feb 2011 17:34:54 -0600 Subject: bad plugin --- nova/virt/xenapi/vmops.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py index 5ab73d562..ba0db22f1 100644 --- a/nova/virt/xenapi/vmops.py +++ b/nova/virt/xenapi/vmops.py @@ -258,7 +258,8 @@ class VMOps(object): #pretty fugly with self._get_snapshot(instance) as snapshot: params = {'host':dest, 'vdi_uuid':snapshot.vdi_uuids[1], - 'dest_name':'base_copy.vhd'} + 'dest_name': 'base_copy.vhd', + 'instance_id': instance.id, } self._session.async_call_plugin('migration', 'transfer_vhd', {'params': pickle.dumps(params)}) @@ -268,7 +269,8 @@ class VMOps(object): vdi_ref, vm_vdi_rec = \ VMHelper.get_vdi_for_vm_safely(self._session, vm_ref) params = {'host':dest, 'vdi_uuid': vm_vdi_rec['uuid'], - 'dest_name': 'cow.vhd'} + 'dest_name': 'cow.vhd', + 'instance_id': instance.id, } self._session.async_call_plugin('migration', 'transfer_vhd', {'params': pickle.dumps(params)}) return snapshot.vdi_uuids[1], vm_vdi_rec['uuid'] -- cgit From 3014c0896202b592858fc1a7fc9c29b92a6f5d1b Mon Sep 17 00:00:00 2001 From: Cerberus Date: Mon, 14 Feb 2011 18:04:07 -0600 Subject: plugin --- plugins/xenserver/xenapi/etc/xapi.d/plugins/migration | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/plugins/xenserver/xenapi/etc/xapi.d/plugins/migration b/plugins/xenserver/xenapi/etc/xapi.d/plugins/migration index c1f5b7528..9c56cb379 100644 --- a/plugins/xenserver/xenapi/etc/xapi.d/plugins/migration +++ b/plugins/xenserver/xenapi/etc/xapi.d/plugins/migration @@ -111,14 +111,18 @@ def transfer_vhd(session, args): vhd_path = "%s.vhd" % vdi_uuid source_path = "%s/%s" % (sr_path, vhd_path) - dest_path = '%s:%sinstance%d/%s' % (host, IMAGE_PATH, instance_id, - dest_name) - rsync_args = ['nohup', RSYNC, '-av', '--progress', - '-e "ssh -o StrictHostKeyChecking=no"', source_path, dest_path] + dest_path = '%sinstance%d/' % (IMAGE_PATH, instance_id) + + dest_path_with_vhd="$s:%s/%s" % (host, dest_path, dest_name) + ssh_cmd = '-e "ssh -o StrictHostKeyChecking=no \'mkdir -p %s\' " ' % dest_path + + rsync_args = ['nohup', RSYNC, '-av', '--progress', ssh_cmd, source_path, + dest_path_with_vhd] if subprocess.call(rsync_args) != 0: raise Exception("Unexpected VHD transfer failure") + if __name__ == '__main__': XenAPIPlugin.dispatch({'transfer_vhd': transfer_vhd, 'move_vhds_into_sr':move_vhds_into_sr, }) -- cgit From e7fe96453760320ef897b9edfc39e057d565e6c0 Mon Sep 17 00:00:00 2001 From: Cerberus Date: Mon, 14 Feb 2011 23:22:37 -0600 Subject: Refactored --- nova/compute/manager.py | 9 ++-- nova/virt/xenapi/vmops.py | 50 ++++++++++++---------- nova/virt/xenapi_conn.py | 2 +- .../xenserver/xenapi/etc/xapi.d/plugins/migration | 17 ++++---- 4 files changed, 43 insertions(+), 35 deletions(-) diff --git a/nova/compute/manager.py b/nova/compute/manager.py index 23d2b80ac..2308c8315 100644 --- a/nova/compute/manager.py +++ b/nova/compute/manager.py @@ -429,14 +429,15 @@ class ComputeManager(manager.Manager): self.db.migration_update(context, migration_id, { 'status': 'migrating', }) - self.driver.migrate_disk_and_power_off(instance_ref, + disk_info = self.driver.migrate_disk_and_power_off(instance_ref, migration_ref['dest_host']) self.db.migration_update(context, migration_id, { 'status': 'post-migrating', }) + #TODO(mdietz): This is where we would update the VM record #after resizing - + service = self.db.service_get_by_host_and_topic(context, migration_ref['dest_host'], FLAGS.compute_topic) topic = self.db.queue_get_for(context, FLAGS.compute_topic, @@ -451,7 +452,7 @@ class ComputeManager(manager.Manager): @exception.wrap_exception @checks_instance_lock - def finish_resize(self, context, instance_id, migration_id): + def finish_resize(self, context, instance_id, migration_id, disk_info): """Completes the migration process by setting up the newly transferred disk and turning on the instance on its new host machine""" migration_ref = self.db.migration_get(context, migration_id) @@ -459,7 +460,7 @@ class ComputeManager(manager.Manager): migration_ref['instance_id']) # this may get passed into the following spawn instead - disk_info = self.driver.attach_disk(instance_ref) + new_disk_info = self.driver.attach_disk(instance_ref, disk_info) self.driver.spawn(instance_ref, disk=disk_info) self.db.migration_update(context, migration_id, diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py index ba0db22f1..127a09ad1 100644 --- a/nova/virt/xenapi/vmops.py +++ b/nova/virt/xenapi/vmops.py @@ -103,17 +103,19 @@ class VMOps(object): vdi_ref) if instance.kernel_id: kernel = VMHelper.fetch_image(self._session, instance.id, - instance.kernel_id, user, project, ImageType.KERNEL_RAMDISK) + instance.kernel_id, user, project, + ImageType.KERNEL_RAMDISK) if instance.ramdisk_id: ramdisk = VMHelper.fetch_image(self._session, instance.id, - instance.ramdisk_id, user, project, ImageType.KERNEL_RAMDISK) + instance.ramdisk_id, user, project, + ImageType.KERNEL_RAMDISK) else: vdi_ref = self._session.call_xenapi('VDI.get_by_uuid', disk) vm_ref = VMHelper.create_vm(self._session, instance, kernel, ramdisk, pv_kernel) - VMHelper.create_vbd(session=self._session, vm_ref=vm_ref, vdi_ref=vdi_ref, - userdevice=0, bootable=True) + VMHelper.create_vbd(session=self._session, vm_ref=vm_ref, + vdi_ref=vdi_ref, userdevice=0, bootable=True) if network_ref: VMHelper.create_vif(self._session, vm_ref, @@ -234,7 +236,7 @@ class VMOps(object): try: template_vm_ref, template_vdi_uuids = VMHelper.create_snapshot( self._session, instance.id, vm_ref, label) - return Snapshot(self, instance, template_vm_ref, + return Snapshot(self, instance, template_vm_ref, template_vdi_uuids) except self.XenAPI.Failure, exc: logging.error(_("Unable to Snapshot %(vm_ref)s: %(exc)s") @@ -254,35 +256,40 @@ class VMOps(object): # identify it via the VBD. The base copy is the parent_uuid returned # from the snapshot creation - #TODO(mdietz): explicitly forcing the base_copy and cow names is - #pretty fugly + base_copy_uuid = cow_uuid = None with self._get_snapshot(instance) as snapshot: - params = {'host':dest, 'vdi_uuid':snapshot.vdi_uuids[1], - 'dest_name': 'base_copy.vhd', + # transfer the base copy + base_copy_uuid = snapshot.vdi_uuids[1] + vdi_ref, vm_vdi_rec = \ + VMHelper.get_vdi_for_vm_safely(self._session, vm_ref) + cow_uuid = vm_vdi_rec['uuid'] + + params = {'host': dest, 'vdi_uuid': base_copy_uuid, 'instance_id': instance.id, } + self._session.async_call_plugin('migration', 'transfer_vhd', {'params': pickle.dumps(params)}) # Now power down the instance and transfer the COW VHD self._shutdown(instance, vm_ref, method='clean') - vdi_ref, vm_vdi_rec = \ - VMHelper.get_vdi_for_vm_safely(self._session, vm_ref) - params = {'host':dest, 'vdi_uuid': vm_vdi_rec['uuid'], - 'dest_name': 'cow.vhd', + params = {'host': dest, 'vdi_uuid': cow_uuid, 'instance_id': instance.id, } self._session.async_call_plugin('migration', 'transfer_vhd', {'params': pickle.dumps(params)}) - return snapshot.vdi_uuids[1], vm_vdi_rec['uuid'] - def attach_disk(self, instance): - vm_ref = VMHelper.lookup(self._session, instance.name) + # TODO(mdietz): we could also consider renaming these to something + # sensible so we don't need to blindly pass around dictionaries + return {'base_copy': base_copy_uuid, 'cow': cow_uuid} + def attach_disk(self, instance, disk_info): + vm_ref = VMHelper.lookup(self._session, instance.name) new_base_copy_uuid = str(uuid.uuid4()) - - params = { 'instance_id': instance.id, - 'new_base_copy_uuid': new_base_copy_uuid, - 'new_cow_uuid': str(uuid.uuid4()) } + params = {'instance_id': instance.id, + 'old_base_copy_uuid': disk_info['base_copy'], + 'old_cow_uuid': disk_info['cow'], + 'new_base_copy_uuid': new_base_copy_uuid, + 'new_cow_uuid': str(uuid.uuid4())} self._session.async_call_plugin('migration', 'move_vhds_into_sr', {'params': pickle.dumps(params)}) @@ -290,7 +297,7 @@ class VMOps(object): # Now we rescan the SR so we find the VHDs VMHelper.scan_sr(self._session) - return new_base_copy_uuid + return new_base_copy_uuid def resize(self, instance, flavor): """Resize a running instance by changing it's RAM and disk size """ @@ -340,7 +347,6 @@ class VMOps(object): raise RuntimeError(resp_dict['message']) return resp_dict['message'] - def _shutdown(self, instance, vm, method='hard'): """Shutdown an instance """ state = self.get_info(instance['name'])['state'] diff --git a/nova/virt/xenapi_conn.py b/nova/virt/xenapi_conn.py index 6869ce8d8..21892ca37 100644 --- a/nova/virt/xenapi_conn.py +++ b/nova/virt/xenapi_conn.py @@ -197,7 +197,7 @@ class XenAPIConnection(object): off the instance copies over the COW disk""" self._vmops.migrate_disk_and_power_off(instance, dest) - def attach_disk(self, instance): + def attach_disk(self, instance, disk_info): """Moves the copied VDIs into the SR""" return self._vmops.attach_disk(instance) diff --git a/plugins/xenserver/xenapi/etc/xapi.d/plugins/migration b/plugins/xenserver/xenapi/etc/xapi.d/plugins/migration index 9c56cb379..3d3ad4e67 100644 --- a/plugins/xenserver/xenapi/etc/xapi.d/plugins/migration +++ b/plugins/xenserver/xenapi/etc/xapi.d/plugins/migration @@ -68,6 +68,9 @@ def move_vhds_into_sr(session, args): params = pickle.loads(exists(args, 'params')) instance_id = params['instance_id'] + old_base_copy_uuid = params['old_base_copy_uuid'] + old_cow_uuid = params['old_cow_uuid'] + new_base_copy_uuid = params['new_base_copy_uuid'] new_cow_uuid = params['new_cow_uuid'] @@ -77,9 +80,9 @@ def move_vhds_into_sr(session, args): # Discover the copied VHDs locally, and then set up paths to copy # them to under the SR source_image_path = "%s/instance%d" % (IMAGE_PATH, instance_id) - source_base_copy_path = "%s/base_copy.vhd" % source_image_path - source_cow_path = "%s/cow.vhd" % source_image_path - + source_base_copy_path = "%s/%s.vhd" % (source_image_path, + old_base_copy_uuid) + source_cow_path = "%s/%s.vhd" % (source_image_path, old_cow_uuid) temp_vhd_path = "%s/instance%d/" % (sr_temp_path, instance_id) new_base_copy_path = "%s/%s.vhd" % (temp_vhd_path, new_base_copy_uuid) @@ -106,18 +109,16 @@ def transfer_vhd(session, args): instance_id = params['instance_id'] host = params['host'] vdi_uuid = params['vdi_uuid'] - dest_name = params['dest_name'] sr_path = get_sr_path(session) vhd_path = "%s.vhd" % vdi_uuid source_path = "%s/%s" % (sr_path, vhd_path) - dest_path = '%sinstance%d/' % (IMAGE_PATH, instance_id) + dest_path = '%s:%sinstance%d/' % (host, IMAGE_PATH, instance_id) - dest_path_with_vhd="$s:%s/%s" % (host, dest_path, dest_name) - ssh_cmd = '-e "ssh -o StrictHostKeyChecking=no \'mkdir -p %s\' " ' % dest_path + ssh_cmd = '-e "ssh -o StrictHostKeyChecking=no " ' rsync_args = ['nohup', RSYNC, '-av', '--progress', ssh_cmd, source_path, - dest_path_with_vhd] + dest_path] if subprocess.call(rsync_args) != 0: raise Exception("Unexpected VHD transfer failure") -- cgit From 4574bcdfe303a76a46eb7579a5a70de4e54cc926 Mon Sep 17 00:00:00 2001 From: Cerberus Date: Mon, 14 Feb 2011 23:58:21 -0600 Subject: Tons o loggin --- nova/compute/manager.py | 1 + nova/virt/xenapi_conn.py | 4 ++-- .../xenserver/xenapi/etc/xapi.d/plugins/migration | 20 +++++++++++++++++++- 3 files changed, 22 insertions(+), 3 deletions(-) diff --git a/nova/compute/manager.py b/nova/compute/manager.py index 2308c8315..6a87bb6f1 100644 --- a/nova/compute/manager.py +++ b/nova/compute/manager.py @@ -447,6 +447,7 @@ class ComputeManager(manager.Manager): 'args': { 'migration_id': migration_id, 'instance_id': instance_id, + 'disk_info': disk_info, }, }) diff --git a/nova/virt/xenapi_conn.py b/nova/virt/xenapi_conn.py index 21892ca37..6d40d4615 100644 --- a/nova/virt/xenapi_conn.py +++ b/nova/virt/xenapi_conn.py @@ -195,11 +195,11 @@ class XenAPIConnection(object): def migrate_disk_and_power_off(self, instance, dest): """Transfers the VHD of a running instance to another host, then shuts off the instance copies over the COW disk""" - self._vmops.migrate_disk_and_power_off(instance, dest) + return self._vmops.migrate_disk_and_power_off(instance, dest) def attach_disk(self, instance, disk_info): """Moves the copied VDIs into the SR""" - return self._vmops.attach_disk(instance) + return self._vmops.attach_disk(instance, disk_info) def suspend(self, instance, callback): """suspend the specified instance""" diff --git a/plugins/xenserver/xenapi/etc/xapi.d/plugins/migration b/plugins/xenserver/xenapi/etc/xapi.d/plugins/migration index 3d3ad4e67..63de5bfba 100644 --- a/plugins/xenserver/xenapi/etc/xapi.d/plugins/migration +++ b/plugins/xenserver/xenapi/etc/xapi.d/plugins/migration @@ -29,6 +29,7 @@ import subprocess import XenAPIPlugin from pluginlib_nova import * +configure_logging('migration') SSH_HOSTS = '/root/.ssh/known_hosts' DEVNULL = '/dev/null' @@ -88,17 +89,27 @@ def move_vhds_into_sr(session, args): new_base_copy_path = "%s/%s.vhd" % (temp_vhd_path, new_base_copy_uuid) new_cow_path = "%s/%s.vhd" % (temp_vhd_path, new_cow_uuid) + logging.debug('Creating temporary SR path' % temp_vhd_path) os.mkdir(temp_vhd_path) + + logging.debug('Moving %s into %s' % (source_base_copy_path, temp_vhd_path)) shutil.move(source_base_copy_path, new_base_copy_path) + + logging.debug('Moving %s into %s' % (source_cow_path, temp_vhd_path)) shutil.move(source_cow_path, new_cow_path) + logging.debug('Cleaning up %s' % source_image_path) os.rmdir(source_image_path) # Link the COW to the base copy + logging.debug('Attaching COW to the base copy...') subprocess.call([VHD_UTIL, 'modify', '-n', new_cow_path, '-p', new_base_copy_path]) + logging.debug('Moving VHDs into SR %s' % sr_path) shutil.move("%s/*.vhd" % temp_vhd_path, sr_path) + + loggin.debug('Cleaning up temporary SR path %s' % temp_vhd_path) os.rmdir(temp_vhd_path) return None @@ -115,12 +126,19 @@ def transfer_vhd(session, args): source_path = "%s/%s" % (sr_path, vhd_path) dest_path = '%s:%sinstance%d/' % (host, IMAGE_PATH, instance_id) + logging.debug("Preparing to transmit %s to %s" % (source_path, + dest_path)) + ssh_cmd = '-e "ssh -o StrictHostKeyChecking=no " ' rsync_args = ['nohup', RSYNC, '-av', '--progress', ssh_cmd, source_path, dest_path] - if subprocess.call(rsync_args) != 0: + logging.debug('rsync %s' % (' '.join(rsync_args, ))) + + rsync_proc = subprocess.POpen(rsync_args) + logging.debug('Rsync output: \n %s' % rsync_proc.communicate()[0]) + if rsync_proc.returncode != 0 raise Exception("Unexpected VHD transfer failure") -- cgit From bf82637cad867b0e8fb6ad868f60c6dcd66d7f97 Mon Sep 17 00:00:00 2001 From: Cerberus Date: Tue, 15 Feb 2011 11:05:20 -0600 Subject: Better host acquisition --- nova/compute/manager.py | 7 ++++--- nova/db/sqlalchemy/migrate_repo/versions/003_cactus.py | 3 ++- nova/db/sqlalchemy/models.py | 3 ++- nova/virt/xenapi_conn.py | 4 ++++ plugins/xenserver/xenapi/etc/xapi.d/plugins/migration | 2 +- 5 files changed, 13 insertions(+), 6 deletions(-) diff --git a/nova/compute/manager.py b/nova/compute/manager.py index 6a87bb6f1..7e929d715 100644 --- a/nova/compute/manager.py +++ b/nova/compute/manager.py @@ -404,12 +404,13 @@ class ComputeManager(manager.Manager): instance_ref = self.db.instance_get(context, instance_id) migration_ref = self.db.migration_create(context, { 'instance_id': instance_id, - 'source_host': instance_ref['host'], - 'dest_host': socket.gethostname(), + 'source_compute': instance_ref['host'], + 'dest_compute': socket.gethostname(), + 'dest_host': self.driver.get_host_ip_addr(), 'status': 'pre-migrating' }) LOG.audit(_('instance %s: migrating to '), instance_id, context=context) service = self.db.service_get_by_host_and_topic(context, - instance_ref['host'], FLAGS.compute_topic) + migration_ref['source_compute'], FLAGS.compute_topic) topic = self.db.queue_get_for(context, FLAGS.compute_topic, service['host']) rpc.cast(context, topic, diff --git a/nova/db/sqlalchemy/migrate_repo/versions/003_cactus.py b/nova/db/sqlalchemy/migrate_repo/versions/003_cactus.py index 02d9177bd..4aab5bdc6 100644 --- a/nova/db/sqlalchemy/migrate_repo/versions/003_cactus.py +++ b/nova/db/sqlalchemy/migrate_repo/versions/003_cactus.py @@ -39,7 +39,8 @@ migrations = Table('migrations', meta, Column('deleted_at', DateTime(timezone=False)), Column('deleted', Boolean(create_constraint=True, name=None)), Column('id', Integer(), primary_key=True, nullable=False), - Column('source_host', String(255)), + Column('source_compute', String(255)), + Column('dest_compute', String(255)), Column('dest_host', String(255)), Column('instance_id', Integer, ForeignKey('instances.id'), nullable=True), diff --git a/nova/db/sqlalchemy/models.py b/nova/db/sqlalchemy/models.py index ebf3a382b..1c84e15dd 100644 --- a/nova/db/sqlalchemy/models.py +++ b/nova/db/sqlalchemy/models.py @@ -370,7 +370,8 @@ class Migration(BASE, NovaBase): """Represents a running host-to-host migration.""" __tablename__ = 'migrations' id = Column(Integer, primary_key=True, nullable=False) - source_host = Column(String(255)) + source_compute = Column(String(255)) + dest_compute = Column(String(255)) dest_host = Column(String(255)) instance_id = Column(Integer, ForeignKey('instances.id'), nullable=True) status = Column(String(255)) #TODO(_cerberus_): enum diff --git a/nova/virt/xenapi_conn.py b/nova/virt/xenapi_conn.py index 6d40d4615..19b5269f5 100644 --- a/nova/virt/xenapi_conn.py +++ b/nova/virt/xenapi_conn.py @@ -224,6 +224,10 @@ class XenAPIConnection(object): def get_ajax_console(self, instance): """Return link to instance's ajax console""" return self._vmops.get_ajax_console(instance) + + def get_host_ip_addr(self): + xs_url = urlparse.urlpase(FLAGS.xenapi_connection_url) + return xs_url.netloc def attach_volume(self, instance_name, device_path, mountpoint): """Attach volume storage to VM instance""" diff --git a/plugins/xenserver/xenapi/etc/xapi.d/plugins/migration b/plugins/xenserver/xenapi/etc/xapi.d/plugins/migration index 63de5bfba..97c970da5 100644 --- a/plugins/xenserver/xenapi/etc/xapi.d/plugins/migration +++ b/plugins/xenserver/xenapi/etc/xapi.d/plugins/migration @@ -136,7 +136,7 @@ def transfer_vhd(session, args): logging.debug('rsync %s' % (' '.join(rsync_args, ))) - rsync_proc = subprocess.POpen(rsync_args) + rsync_proc = subprocess.Popen(rsync_args) logging.debug('Rsync output: \n %s' % rsync_proc.communicate()[0]) if rsync_proc.returncode != 0 raise Exception("Unexpected VHD transfer failure") -- cgit From 03a8d1baae00a4150a02ac2f0b04c413dd3b00e0 Mon Sep 17 00:00:00 2001 From: Cerberus Date: Tue, 15 Feb 2011 11:19:27 -0600 Subject: derp --- nova/compute/manager.py | 2 +- nova/virt/xenapi_conn.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/nova/compute/manager.py b/nova/compute/manager.py index 7e929d715..546d07a09 100644 --- a/nova/compute/manager.py +++ b/nova/compute/manager.py @@ -440,7 +440,7 @@ class ComputeManager(manager.Manager): #after resizing service = self.db.service_get_by_host_and_topic(context, - migration_ref['dest_host'], FLAGS.compute_topic) + migration_ref['dest_compute'], FLAGS.compute_topic) topic = self.db.queue_get_for(context, FLAGS.compute_topic, service['host']) rpc.cast(context, topic, diff --git a/nova/virt/xenapi_conn.py b/nova/virt/xenapi_conn.py index 19b5269f5..2671f1a7b 100644 --- a/nova/virt/xenapi_conn.py +++ b/nova/virt/xenapi_conn.py @@ -226,7 +226,7 @@ class XenAPIConnection(object): return self._vmops.get_ajax_console(instance) def get_host_ip_addr(self): - xs_url = urlparse.urlpase(FLAGS.xenapi_connection_url) + xs_url = urlparse.urlparse(FLAGS.xenapi_connection_url) return xs_url.netloc def attach_volume(self, instance_name, device_path, mountpoint): -- cgit From c97d408842a4a5a8e9d379acc13c9c1f5871827f Mon Sep 17 00:00:00 2001 From: Cerberus Date: Tue, 15 Feb 2011 14:10:43 -0600 Subject: Plugin changes --- nova/virt/xenapi/vmops.py | 11 +++++++---- plugins/xenserver/xenapi/etc/xapi.d/plugins/migration | 12 +++++++----- 2 files changed, 14 insertions(+), 9 deletions(-) diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py index 127a09ad1..882d52f38 100644 --- a/nova/virt/xenapi/vmops.py +++ b/nova/virt/xenapi/vmops.py @@ -267,16 +267,18 @@ class VMOps(object): params = {'host': dest, 'vdi_uuid': base_copy_uuid, 'instance_id': instance.id, } - self._session.async_call_plugin('migration', 'transfer_vhd', + task = self._session.async_call_plugin('migration', 'transfer_vhd', {'params': pickle.dumps(params)}) + self._session.wait_for_task(instance.id, task) # Now power down the instance and transfer the COW VHD self._shutdown(instance, vm_ref, method='clean') params = {'host': dest, 'vdi_uuid': cow_uuid, 'instance_id': instance.id, } - self._session.async_call_plugin('migration', 'transfer_vhd', + task = self._session.async_call_plugin('migration', 'transfer_vhd', {'params': pickle.dumps(params)}) + self._session.wait_for_task(instance.id, task) # TODO(mdietz): we could also consider renaming these to something # sensible so we don't need to blindly pass around dictionaries @@ -291,8 +293,9 @@ class VMOps(object): 'new_base_copy_uuid': new_base_copy_uuid, 'new_cow_uuid': str(uuid.uuid4())} - self._session.async_call_plugin('migration', 'move_vhds_into_sr', - {'params': pickle.dumps(params)}) + task = self._session.async_call_plugin('migration', + 'move_vhds_into_sr', {'params': pickle.dumps(params)}) + self._session.wait_for_task(instance.id, task) # Now we rescan the SR so we find the VHDs VMHelper.scan_sr(self._session) diff --git a/plugins/xenserver/xenapi/etc/xapi.d/plugins/migration b/plugins/xenserver/xenapi/etc/xapi.d/plugins/migration index 97c970da5..4a4ed0e73 100644 --- a/plugins/xenserver/xenapi/etc/xapi.d/plugins/migration +++ b/plugins/xenserver/xenapi/etc/xapi.d/plugins/migration @@ -129,17 +129,19 @@ def transfer_vhd(session, args): logging.debug("Preparing to transmit %s to %s" % (source_path, dest_path)) - ssh_cmd = '-e "ssh -o StrictHostKeyChecking=no " ' + ssh_cmd = '-e \'ssh -o StrictHostKeyChecking=no\' ' - rsync_args = ['nohup', RSYNC, '-av', '--progress', ssh_cmd, source_path, - dest_path] + rsync_args = ['nohup > /root/instance%d_progress' % instance_id, RSYNC, + '-av', '--progress', ssh_cmd, source_path, dest_path] logging.debug('rsync %s' % (' '.join(rsync_args, ))) - rsync_proc = subprocess.Popen(rsync_args) + rsync_proc = subprocess.Popen(rsync_args, stdout=subprocess.PIPE) logging.debug('Rsync output: \n %s' % rsync_proc.communicate()[0]) - if rsync_proc.returncode != 0 + logging.debug('Rsync return: %d' % rsync_proc.returncode) + if rsync_proc.returncode != 0: raise Exception("Unexpected VHD transfer failure") + return "" if __name__ == '__main__': -- cgit From 00f2905a5debc5835b742dab8dce003f53e33fc2 Mon Sep 17 00:00:00 2001 From: Cerberus Date: Tue, 15 Feb 2011 14:29:31 -0600 Subject: plugin lol --- plugins/xenserver/xenapi/etc/xapi.d/plugins/migration | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/plugins/xenserver/xenapi/etc/xapi.d/plugins/migration b/plugins/xenserver/xenapi/etc/xapi.d/plugins/migration index 4a4ed0e73..bfc2a2ed4 100644 --- a/plugins/xenserver/xenapi/etc/xapi.d/plugins/migration +++ b/plugins/xenserver/xenapi/etc/xapi.d/plugins/migration @@ -89,7 +89,7 @@ def move_vhds_into_sr(session, args): new_base_copy_path = "%s/%s.vhd" % (temp_vhd_path, new_base_copy_uuid) new_cow_path = "%s/%s.vhd" % (temp_vhd_path, new_cow_uuid) - logging.debug('Creating temporary SR path' % temp_vhd_path) + logging.debug('Creating temporary SR path %s' % temp_vhd_path) os.mkdir(temp_vhd_path) logging.debug('Moving %s into %s' % (source_base_copy_path, temp_vhd_path)) @@ -129,10 +129,10 @@ def transfer_vhd(session, args): logging.debug("Preparing to transmit %s to %s" % (source_path, dest_path)) - ssh_cmd = '-e \'ssh -o StrictHostKeyChecking=no\' ' + ssh_cmd = 'ssh -o StrictHostKeyChecking=no' - rsync_args = ['nohup > /root/instance%d_progress' % instance_id, RSYNC, - '-av', '--progress', ssh_cmd, source_path, dest_path] + rsync_args = ['nohup', RSYNC, '-av', '--progress', '-e', ssh_cmd, + source_path, dest_path] logging.debug('rsync %s' % (' '.join(rsync_args, ))) -- cgit From 9f77e0a46cac2ebaf9a18c4a175099b208db1adb Mon Sep 17 00:00:00 2001 From: Cerberus Date: Tue, 15 Feb 2011 15:27:23 -0600 Subject: More plugin lol --- plugins/xenserver/xenapi/etc/xapi.d/plugins/migration | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/plugins/xenserver/xenapi/etc/xapi.d/plugins/migration b/plugins/xenserver/xenapi/etc/xapi.d/plugins/migration index bfc2a2ed4..cc72b5d26 100644 --- a/plugins/xenserver/xenapi/etc/xapi.d/plugins/migration +++ b/plugins/xenserver/xenapi/etc/xapi.d/plugins/migration @@ -90,7 +90,7 @@ def move_vhds_into_sr(session, args): new_cow_path = "%s/%s.vhd" % (temp_vhd_path, new_cow_uuid) logging.debug('Creating temporary SR path %s' % temp_vhd_path) - os.mkdir(temp_vhd_path) + os.makedirs(temp_vhd_path) logging.debug('Moving %s into %s' % (source_base_copy_path, temp_vhd_path)) shutil.move(source_base_copy_path, new_base_copy_path) @@ -107,11 +107,12 @@ def move_vhds_into_sr(session, args): new_base_copy_path]) logging.debug('Moving VHDs into SR %s' % sr_path) - shutil.move("%s/*.vhd" % temp_vhd_path, sr_path) + shutil.move("%s/%s.vhd" % (temp_vhd_path, new_base_copy_uuid), sr_path) + shutil.move("%s/%s.vhd" % (temp_vhd_path, new_cow_uuid), sr_path) - loggin.debug('Cleaning up temporary SR path %s' % temp_vhd_path) + logginig.debug('Cleaning up temporary SR path %s' % temp_vhd_path) os.rmdir(temp_vhd_path) - return None + return "" def transfer_vhd(session, args): -- cgit From f6bf7e8c1e2481e870ed4baa9f2a6aa8001b5514 Mon Sep 17 00:00:00 2001 From: Cerberus Date: Tue, 15 Feb 2011 16:46:17 -0600 Subject: fail --- nova/compute/manager.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/nova/compute/manager.py b/nova/compute/manager.py index 546d07a09..19e1d9f46 100644 --- a/nova/compute/manager.py +++ b/nova/compute/manager.py @@ -402,10 +402,14 @@ class ComputeManager(manager.Manager): host, possibly changing the RAM and disk size in the process""" context = context.elevated() instance_ref = self.db.instance_get(context, instance_id) + if instance_ref['host'] == FLAGS.host: + raise exception.Error(_( + 'Migration error: destination same as source!')) + migration_ref = self.db.migration_create(context, { 'instance_id': instance_id, 'source_compute': instance_ref['host'], - 'dest_compute': socket.gethostname(), + 'dest_compute': FLAGS.host, 'dest_host': self.driver.get_host_ip_addr(), 'status': 'pre-migrating' }) LOG.audit(_('instance %s: migrating to '), instance_id, context=context) @@ -463,7 +467,7 @@ class ComputeManager(manager.Manager): # this may get passed into the following spawn instead new_disk_info = self.driver.attach_disk(instance_ref, disk_info) - self.driver.spawn(instance_ref, disk=disk_info) + self.driver.spawn(instance_ref, disk=new_disk_info) self.db.migration_update(context, migration_id, {'status': 'finished', }) -- cgit From a6ea6759450aab7eb021e202c68e5301667c74a9 Mon Sep 17 00:00:00 2001 From: Cerberus Date: Tue, 15 Feb 2011 17:58:57 -0600 Subject: foo --- nova/virt/xenapi/vmops.py | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py index 882d52f38..c2f5ddc41 100644 --- a/nova/virt/xenapi/vmops.py +++ b/nova/virt/xenapi/vmops.py @@ -97,21 +97,22 @@ class VMOps(object): vdi_uuid = VMHelper.fetch_image(self._session, instance.id, instance.image_id, user, project, disk_image_type) vdi_ref = self._session.call_xenapi('VDI.get_by_uuid', vdi_uuid) - #Have a look at the VDI and see if it has a PV kernel - if not instance.kernel_id: - pv_kernel = VMHelper.lookup_image(self._session, instance.id, - vdi_ref) - if instance.kernel_id: - kernel = VMHelper.fetch_image(self._session, instance.id, - instance.kernel_id, user, project, - ImageType.KERNEL_RAMDISK) - if instance.ramdisk_id: - ramdisk = VMHelper.fetch_image(self._session, instance.id, - instance.ramdisk_id, user, project, - ImageType.KERNEL_RAMDISK) else: vdi_ref = self._session.call_xenapi('VDI.get_by_uuid', disk) + #Have a look at the VDI and see if it has a PV kernel + if not instance.kernel_id: + pv_kernel = VMHelper.lookup_image(self._session, instance.id, + vdi_ref) + if instance.kernel_id: + kernel = VMHelper.fetch_image(self._session, instance.id, + instance.kernel_id, user, project, + ImageType.KERNEL_RAMDISK) + if instance.ramdisk_id: + ramdisk = VMHelper.fetch_image(self._session, instance.id, + instance.ramdisk_id, user, project, + ImageType.KERNEL_RAMDISK) + vm_ref = VMHelper.create_vm(self._session, instance, kernel, ramdisk, pv_kernel) VMHelper.create_vbd(session=self._session, vm_ref=vm_ref, -- cgit From cd5aba9d1d00d9daad87efd89f78e49079bee2c7 Mon Sep 17 00:00:00 2001 From: Cerberus Date: Tue, 15 Feb 2011 18:02:57 -0600 Subject: foo --- plugins/xenserver/xenapi/etc/xapi.d/plugins/migration | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/xenserver/xenapi/etc/xapi.d/plugins/migration b/plugins/xenserver/xenapi/etc/xapi.d/plugins/migration index cc72b5d26..5bf0fe994 100644 --- a/plugins/xenserver/xenapi/etc/xapi.d/plugins/migration +++ b/plugins/xenserver/xenapi/etc/xapi.d/plugins/migration @@ -110,7 +110,7 @@ def move_vhds_into_sr(session, args): shutil.move("%s/%s.vhd" % (temp_vhd_path, new_base_copy_uuid), sr_path) shutil.move("%s/%s.vhd" % (temp_vhd_path, new_cow_uuid), sr_path) - logginig.debug('Cleaning up temporary SR path %s' % temp_vhd_path) + logging.debug('Cleaning up temporary SR path %s' % temp_vhd_path) os.rmdir(temp_vhd_path) return "" -- cgit From bb98e2055002ff3ed2099f60bbe4058d5f5c7b35 Mon Sep 17 00:00:00 2001 From: Cerberus Date: Tue, 15 Feb 2011 23:10:29 -0600 Subject: hurr durr --- nova/virt/xenapi/vmops.py | 5 +++-- plugins/xenserver/xenapi/etc/xapi.d/plugins/migration | 3 ++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py index c2f5ddc41..7a176442a 100644 --- a/nova/virt/xenapi/vmops.py +++ b/nova/virt/xenapi/vmops.py @@ -288,11 +288,12 @@ class VMOps(object): def attach_disk(self, instance, disk_info): vm_ref = VMHelper.lookup(self._session, instance.name) new_base_copy_uuid = str(uuid.uuid4()) + new_cow_uuid = str(uuid.uuid4()) params = {'instance_id': instance.id, 'old_base_copy_uuid': disk_info['base_copy'], 'old_cow_uuid': disk_info['cow'], 'new_base_copy_uuid': new_base_copy_uuid, - 'new_cow_uuid': str(uuid.uuid4())} + 'new_cow_uuid': new_cow_uuid, } task = self._session.async_call_plugin('migration', 'move_vhds_into_sr', {'params': pickle.dumps(params)}) @@ -301,7 +302,7 @@ class VMOps(object): # Now we rescan the SR so we find the VHDs VMHelper.scan_sr(self._session) - return new_base_copy_uuid + return new_cow_uuid def resize(self, instance, flavor): """Resize a running instance by changing it's RAM and disk size """ diff --git a/plugins/xenserver/xenapi/etc/xapi.d/plugins/migration b/plugins/xenserver/xenapi/etc/xapi.d/plugins/migration index 5bf0fe994..7a6eefda2 100644 --- a/plugins/xenserver/xenapi/etc/xapi.d/plugins/migration +++ b/plugins/xenserver/xenapi/etc/xapi.d/plugins/migration @@ -102,7 +102,8 @@ def move_vhds_into_sr(session, args): os.rmdir(source_image_path) # Link the COW to the base copy - logging.debug('Attaching COW to the base copy...') + logging.debug('Attaching COW to the base copy %s -> %s' % + (new_cow_path, new_base_copy_path)) subprocess.call([VHD_UTIL, 'modify', '-n', new_cow_path, '-p', new_base_copy_path]) -- cgit From 98b038c6878772f6b272cb169b1c74bd7c9838b8 Mon Sep 17 00:00:00 2001 From: Cerberus Date: Tue, 15 Feb 2011 23:56:00 -0600 Subject: Foo --- nova/api/openstack/servers.py | 12 ++++++++++-- nova/compute/api.py | 20 +++++++++++++++----- nova/compute/manager.py | 16 +++------------- nova/db/sqlalchemy/api.py | 1 + 4 files changed, 29 insertions(+), 20 deletions(-) diff --git a/nova/api/openstack/servers.py b/nova/api/openstack/servers.py index 06a40e92c..83b421127 100644 --- a/nova/api/openstack/servers.py +++ b/nova/api/openstack/servers.py @@ -207,10 +207,18 @@ class Controller(wsgi.Controller): return faults.Fault(exc.HTTPNotImplemented()) def _action_confirm_resize(self, input_dict, req, id): - return faults.Fault(exc.HTTPNotImplemented()) + try: + self.compute_api.confirm_resize(req.environ['nova.context'], id) + except: + return faults.Fault(exc.HTTPBadRequest()) + return exc.HTTPNoContent() def _action_revert_resize(self, input_dict, req, id): - return faults.Fault(exc.HTTPNotImplemented()) + try: + self.compute_api.confirm_resize(req.environ['nova.context'], id) + except: + return faults.Fault(exc.HTTPBadRequest()) + return exc.HTTPAccepted() def _action_rebuild(self, input_dict, req, id): return faults.Fault(exc.HTTPNotImplemented()) diff --git a/nova/compute/api.py b/nova/compute/api.py index 6b2628378..b8c4a8597 100644 --- a/nova/compute/api.py +++ b/nova/compute/api.py @@ -401,16 +401,26 @@ class API(base.Base): def revert_resize(self, context, instance_id): """Reverts a resize, deleting the 'new' instance in the process""" + context = context.elevated() instance_ref = self.db.instance_get(instance_id) - self._cast_compute_message('revert_resize', context, instance_id, - instance_ref['host']) + self._cast_compute_message('revert_resize', context, instance_id) def confirm_resize(self, context, instance_id): """Confirms a migration/resize, deleting the 'old' instance in the process.""" - migration_ref = self.db.get_migration_by_instance_id(instance_id) - self._cast_compute_message('confirm_resize', context, instance_id, - migration_ref['source_host']) + context = context.elevated() + migration_ref = self.db.migration_get_by_instance_id(context, + instance_id) + if migration_ref['status'] != 'finished': + raise exception.Error(_("Migration has incorrect status %s" % + migration_ref['status'])) + instance_ref = self.db.instance_get(context, instance_id) + + self._cast_compute_message('terminate_instance', context, instance_id, + migration_ref['source_compute']) + + self.db.instance_update(context, instance_id, + {'host': migration_ref['dest_compute'], }) def resize(self, context, instance_id, flavor): """Resize a running instance.""" diff --git a/nova/compute/manager.py b/nova/compute/manager.py index 19e1d9f46..169509163 100644 --- a/nova/compute/manager.py +++ b/nova/compute/manager.py @@ -379,12 +379,7 @@ class ComputeManager(manager.Manager): def _update_state_callback(self, context, instance_id, result): """Update instance state when async task completes.""" self._update_state(context, instance_id) - - @exception.wrap_exception - @checks_instance_lock - def confirm_resize(self, context, instance_id): - """Destroys the old instance on the source machine""" - pass + @exception.wrap_exception @checks_instance_lock @@ -413,10 +408,8 @@ class ComputeManager(manager.Manager): 'dest_host': self.driver.get_host_ip_addr(), 'status': 'pre-migrating' }) LOG.audit(_('instance %s: migrating to '), instance_id, context=context) - service = self.db.service_get_by_host_and_topic(context, - migration_ref['source_compute'], FLAGS.compute_topic) topic = self.db.queue_get_for(context, FLAGS.compute_topic, - service['host']) + instance_ref['host']) rpc.cast(context, topic, { 'method': 'resize_instance', 'args': { @@ -446,7 +439,7 @@ class ComputeManager(manager.Manager): service = self.db.service_get_by_host_and_topic(context, migration_ref['dest_compute'], FLAGS.compute_topic) topic = self.db.queue_get_for(context, FLAGS.compute_topic, - service['host']) + migration_ref['dest_compute']) rpc.cast(context, topic, { 'method': 'finish_resize', 'args': { @@ -472,9 +465,6 @@ class ComputeManager(manager.Manager): self.db.migration_update(context, migration_id, {'status': 'finished', }) - # Cleans up any transferred files and unmounts things - self.driver.cleanup_disk_transfer(context, instance_ref['id']) - @exception.wrap_exception @checks_instance_lock def pause_instance(self, context, instance_id): diff --git a/nova/db/sqlalchemy/api.py b/nova/db/sqlalchemy/api.py index 861d13716..1b6eaf138 100644 --- a/nova/db/sqlalchemy/api.py +++ b/nova/db/sqlalchemy/api.py @@ -1935,6 +1935,7 @@ def migration_update(context, id, values): with session.begin(): migration = migration_get(context, id) migration.update(values) + migration.save() return migration -- cgit From 8e536500e83b311bf8d006ca23234c50962dc6aa Mon Sep 17 00:00:00 2001 From: Cerberus Date: Wed, 16 Feb 2011 00:06:29 -0600 Subject: I fail at sessions --- nova/compute/manager.py | 1 - nova/db/sqlalchemy/api.py | 9 +++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/nova/compute/manager.py b/nova/compute/manager.py index 169509163..b405e3763 100644 --- a/nova/compute/manager.py +++ b/nova/compute/manager.py @@ -435,7 +435,6 @@ class ComputeManager(manager.Manager): #TODO(mdietz): This is where we would update the VM record #after resizing - service = self.db.service_get_by_host_and_topic(context, migration_ref['dest_compute'], FLAGS.compute_topic) topic = self.db.queue_get_for(context, FLAGS.compute_topic, diff --git a/nova/db/sqlalchemy/api.py b/nova/db/sqlalchemy/api.py index 1b6eaf138..f96430e67 100644 --- a/nova/db/sqlalchemy/api.py +++ b/nova/db/sqlalchemy/api.py @@ -1933,15 +1933,16 @@ def migration_create(context, values): def migration_update(context, id, values): session = get_session() with session.begin(): - migration = migration_get(context, id) + migration = migration_get(context, id, session=session) migration.update(values) - migration.save() + migration.save(session=session) return migration @require_admin_context -def migration_get(context, id): - session = get_session() +def migration_get(context, id, session=None): + if not session: + session = get_session() result = session.query(models.Migration).\ filter_by(id=id).first() if not result: -- cgit From c735796e0668b2bf7c45eeef6396a3fb33d22d6e Mon Sep 17 00:00:00 2001 From: Cerberus Date: Wed, 16 Feb 2011 00:14:26 -0600 Subject: I fail at sessions --- nova/compute/api.py | 2 +- nova/db/api.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/nova/compute/api.py b/nova/compute/api.py index b8c4a8597..3fb852ab0 100644 --- a/nova/compute/api.py +++ b/nova/compute/api.py @@ -409,7 +409,7 @@ class API(base.Base): """Confirms a migration/resize, deleting the 'old' instance in the process.""" context = context.elevated() - migration_ref = self.db.migration_get_by_instance_id(context, + migration_ref = self.db.migration_get_by_instance(context, instance_id) if migration_ref['status'] != 'finished': raise exception.Error(_("Migration has incorrect status %s" % diff --git a/nova/db/api.py b/nova/db/api.py index 887f57885..9ed5efedb 100644 --- a/nova/db/api.py +++ b/nova/db/api.py @@ -271,9 +271,9 @@ def migration_get(context, migration_id): """Finds a migration by the id""" return IMPL.migration_get(context, migration_id) -def migration_get_by_instance_id(context, instance_id): +def migration_get_by_instance(context, instance_id): """Finds a migration by the instance id its migrating""" - return IMPL.migration_get_by_instance_id(context, instance_id) + return IMPL.migration_get_by_instance(context, instance_id) #################### -- cgit From 4375069b6635d6ccd87231cb7d9f5b17708ffb1a Mon Sep 17 00:00:00 2001 From: Josh Kearney Date: Wed, 16 Feb 2011 11:11:49 -0600 Subject: Stubbed out flavor create/delete API calls --- nova/api/openstack/flavors.py | 9 ++++++++- nova/tests/api/openstack/test_flavors.py | 10 ++++++++++ 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/nova/api/openstack/flavors.py b/nova/api/openstack/flavors.py index 9b674afbd..215f0b8a6 100644 --- a/nova/api/openstack/flavors.py +++ b/nova/api/openstack/flavors.py @@ -42,7 +42,6 @@ class Controller(wsgi.Controller): def detail(self, req): """Return all flavors in detail.""" items = [self.show(req, id)['flavor'] for id in self._all_ids()] - items = common.limited(items, req) return dict(flavors=items) def show(self, req, id): @@ -57,6 +56,14 @@ class Controller(wsgi.Controller): return dict(flavor=values) raise faults.Fault(exc.HTTPNotFound()) + def create(self, req): + """Create a flavor.""" + print "CREATE! %s" % req + + def delete(self, req, id): + """Delete a flavor.""" + print "DELETE! %s %s" % (req, id) + def _all_ids(self): """Return the list of all flavorids.""" # FIXME(kpepple) do we really need admin context here ? diff --git a/nova/tests/api/openstack/test_flavors.py b/nova/tests/api/openstack/test_flavors.py index 532b34e1b..7bfc46e0b 100644 --- a/nova/tests/api/openstack/test_flavors.py +++ b/nova/tests/api/openstack/test_flavors.py @@ -43,7 +43,17 @@ class FlavorsTest(unittest.TestCase): def test_get_flavor_list(self): req = webob.Request.blank('/v1.0/flavors') res = req.get_response(fakes.wsgi_app()) + self.assertEqual(res.status_int, 200) + def test_create_flavor(self): + req = webob.Request.blank("/v1.0/flavors/create/test") + res = req.get_response(fakes.wsgi_app()) + self.assertEqual(res.status_int, 200) + + def test_delete_flavor(self): + req = webob.Request.blank("/v1.0/flavors/delete/test") + res = req.get_response(fakes.wsgi_app()) + self.assertEqual(res.status_int, 200) if __name__ == '__main__': unittest.main() -- cgit From 879845496a50477ebc2709291c159ae1e8d5aa2a Mon Sep 17 00:00:00 2001 From: Cerberus Date: Wed, 16 Feb 2011 13:47:14 -0600 Subject: Derp --- nova/compute/api.py | 26 +++++++++++++++++--------- nova/compute/manager.py | 32 +++++++++++++++++++++++++++++--- nova/db/sqlalchemy/api.py | 5 +++-- 3 files changed, 49 insertions(+), 14 deletions(-) diff --git a/nova/compute/api.py b/nova/compute/api.py index 3fb852ab0..58dea5db6 100644 --- a/nova/compute/api.py +++ b/nova/compute/api.py @@ -402,22 +402,30 @@ class API(base.Base): def revert_resize(self, context, instance_id): """Reverts a resize, deleting the 'new' instance in the process""" context = context.elevated() - instance_ref = self.db.instance_get(instance_id) - self._cast_compute_message('revert_resize', context, instance_id) + migration_ref = self.db.migration_get_by_instance_and_status(context, + instance_id, 'finished') + if not migration_ref: + raise exception.Error(_("No finished migrations found for + instance")) + + params = { 'migration_id': migration_ref['id']) + self._cast_compute_message('revert_resize', context, instance_id, + migration_ref['dest_compute'], params=params) def confirm_resize(self, context, instance_id): """Confirms a migration/resize, deleting the 'old' instance in the process.""" context = context.elevated() - migration_ref = self.db.migration_get_by_instance(context, - instance_id) - if migration_ref['status'] != 'finished': - raise exception.Error(_("Migration has incorrect status %s" % - migration_ref['status'])) + migration_ref = self.db.migration_get_by_instance_and_status(context, + instance_id, 'finished') + if not migration_ref: + raise exception.Error(_("No finished migrations found for + instance")) instance_ref = self.db.instance_get(context, instance_id) - self._cast_compute_message('terminate_instance', context, instance_id, - migration_ref['source_compute']) + params = { 'migration_id': migration_ref['id']) + self._cast_compute_message('confirm_resize', context, instance_id, + migration_ref['source_compute'], params=param) self.db.instance_update(context, instance_id, {'host': migration_ref['dest_compute'], }) diff --git a/nova/compute/manager.py b/nova/compute/manager.py index b405e3763..c05edd140 100644 --- a/nova/compute/manager.py +++ b/nova/compute/manager.py @@ -380,15 +380,41 @@ class ComputeManager(manager.Manager): """Update instance state when async task completes.""" self._update_state(context, instance_id) + @exception.wrap_exception + @echecks_instance_lock + def confirm_resize(self, context, instance_id, migration_id): + """ Destroys the source instance """ + context = context.elevated() + instance_ref = self.db.instance_get(context, instance_id) + migration_ref = self.db.migration_get(context, migration_id) + self.driver.destroy(instance_ref) + self.db.migration_update(context, migration_id, + { 'status': 'confirmed' }) @exception.wrap_exception @checks_instance_lock - def revert_resize(self, context, instance_id): + def revert_resize(self, context, instance_id, migration_id): """Destroys the new instance on the destination machine, reverts the model changes, and powers on the old instance on the source machine""" - pass - + instance_ref = self.db.instance_get(context, instance_id) + migration_ref = self.db.migration_get(context, migration_id) + + if migration_ref['source_compute'] == instance_ref['host']: + self.driver.power_on(instance_ref) + self.db.migration_update(context, migration_id, + { 'status': 'reverted' }) + else: + self.driver.destroy(instance_ref) + topic = self.db.queue_get_for(context, FLAGS.compute_topic, + instance_ref['host']) + rpc.cast(context, topic, + { 'method': 'resize_instance', + 'args': { + 'migration_id': migration_ref['id'], + 'instance_id': instance_id, + }, + }) @exception.wrap_exception @checks_instance_lock diff --git a/nova/db/sqlalchemy/api.py b/nova/db/sqlalchemy/api.py index f96430e67..62484805c 100644 --- a/nova/db/sqlalchemy/api.py +++ b/nova/db/sqlalchemy/api.py @@ -1952,10 +1952,11 @@ def migration_get(context, id, session=None): @require_admin_context -def migration_get_by_instance(context, instance_id): +def migration_get_by_instance_and_status(context, instance_id, status): session = get_session() result = session.query(models.Migration).\ - filter_by(instance_id=instance_id).first() + filter_by(instance_id=instance_id). + filter_by(status=status).first() if not result: raise exception.NotFound(_("No migration found with instance id %s") % migration_id) -- cgit From 905cf54f06f6dde95039599ae5ea30d2f070f398 Mon Sep 17 00:00:00 2001 From: Cerberus Date: Wed, 16 Feb 2011 13:53:47 -0600 Subject: Typos --- nova/compute/api.py | 8 ++++---- nova/compute/manager.py | 2 +- nova/db/sqlalchemy/api.py | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/nova/compute/api.py b/nova/compute/api.py index 4262a771b..2f39b8b47 100644 --- a/nova/compute/api.py +++ b/nova/compute/api.py @@ -405,10 +405,10 @@ class API(base.Base): migration_ref = self.db.migration_get_by_instance_and_status(context, instance_id, 'finished') if not migration_ref: - raise exception.Error(_("No finished migrations found for + raise exception.Error(_("No finished migrations found for \ instance")) - params = { 'migration_id': migration_ref['id']) + params = { 'migration_id': migration_ref['id'] } self._cast_compute_message('revert_resize', context, instance_id, migration_ref['dest_compute'], params=params) @@ -419,11 +419,11 @@ class API(base.Base): migration_ref = self.db.migration_get_by_instance_and_status(context, instance_id, 'finished') if not migration_ref: - raise exception.Error(_("No finished migrations found for + raise exception.Error(_("No finished migrations found for \ instance")) instance_ref = self.db.instance_get(context, instance_id) - params = { 'migration_id': migration_ref['id']) + params = { 'migration_id': migration_ref['id'] } self._cast_compute_message('confirm_resize', context, instance_id, migration_ref['source_compute'], params=param) diff --git a/nova/compute/manager.py b/nova/compute/manager.py index d78318bec..4bab7081a 100644 --- a/nova/compute/manager.py +++ b/nova/compute/manager.py @@ -381,7 +381,7 @@ class ComputeManager(manager.Manager): self._update_state(context, instance_id) @exception.wrap_exception - @echecks_instance_lock + @checks_instance_lock def confirm_resize(self, context, instance_id, migration_id): """ Destroys the source instance """ context = context.elevated() diff --git a/nova/db/sqlalchemy/api.py b/nova/db/sqlalchemy/api.py index b6fb57df7..f4dc8a630 100644 --- a/nova/db/sqlalchemy/api.py +++ b/nova/db/sqlalchemy/api.py @@ -1961,7 +1961,7 @@ def migration_get(context, id, session=None): def migration_get_by_instance_and_status(context, instance_id, status): session = get_session() result = session.query(models.Migration).\ - filter_by(instance_id=instance_id). + filter_by(instance_id=instance_id).\ filter_by(status=status).first() if not result: raise exception.NotFound(_("No migration found with instance id %s") -- cgit From 8f206774ee75c2d96c15dd2c604ae5da9601d91f Mon Sep 17 00:00:00 2001 From: Cerberus Date: Wed, 16 Feb 2011 17:02:57 -0600 Subject: Better exceptions --- nova/api/openstack/servers.py | 15 +++++++++------ nova/db/api.py | 2 +- 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/nova/api/openstack/servers.py b/nova/api/openstack/servers.py index 83b421127..2fc105d07 100644 --- a/nova/api/openstack/servers.py +++ b/nova/api/openstack/servers.py @@ -209,15 +209,17 @@ class Controller(wsgi.Controller): def _action_confirm_resize(self, input_dict, req, id): try: self.compute_api.confirm_resize(req.environ['nova.context'], id) - except: - return faults.Fault(exc.HTTPBadRequest()) + except Exception, e: + LOG.exception(_("Error in confirm-resize %s"), e) + return faults.Fault(exc.HTTPBadRequest(e)) return exc.HTTPNoContent() def _action_revert_resize(self, input_dict, req, id): try: self.compute_api.confirm_resize(req.environ['nova.context'], id) - except: - return faults.Fault(exc.HTTPBadRequest()) + except Exception, e: + LOG.exception(_("Error in revert-resize %s"), e) + return faults.Fault(exc.HTTPBadRequest(e)) return exc.HTTPAccepted() def _action_rebuild(self, input_dict, req, id): @@ -229,8 +231,9 @@ class Controller(wsgi.Controller): flavor_id = input_dict['resize']['flavorId'] self.compute_api.resize(req.environ['nova.context'], id, flavor_id) - except: - return faults.Fault(exc.HTTPUnprocessableEntity()) + except Exception, e: + LOG.exception(_("Error in resize %s"), e) + return faults.Fault(exc.HTTPUnprocessableEntity(e)) return faults.Fault(exc.HTTPAccepted()) diff --git a/nova/db/api.py b/nova/db/api.py index 9ed5efedb..295d1a90a 100644 --- a/nova/db/api.py +++ b/nova/db/api.py @@ -271,7 +271,7 @@ def migration_get(context, migration_id): """Finds a migration by the id""" return IMPL.migration_get(context, migration_id) -def migration_get_by_instance(context, instance_id): +def migration_get_by_instance_and_status(context, instance_id, status): """Finds a migration by the instance id its migrating""" return IMPL.migration_get_by_instance(context, instance_id) -- cgit From c01519112245f5e991ab438fe983bf9331d4e952 Mon Sep 17 00:00:00 2001 From: Cerberus Date: Wed, 16 Feb 2011 17:51:43 -0600 Subject: fixed --- nova/compute/api.py | 5 ++++- nova/compute/manager.py | 2 -- nova/db/api.py | 3 ++- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/nova/compute/api.py b/nova/compute/api.py index 2f39b8b47..635632b73 100644 --- a/nova/compute/api.py +++ b/nova/compute/api.py @@ -425,7 +425,10 @@ class API(base.Base): params = { 'migration_id': migration_ref['id'] } self._cast_compute_message('confirm_resize', context, instance_id, - migration_ref['source_compute'], params=param) + migration_ref['source_compute'], params=params) + + self.db.migration_update(context, migration_id, + { 'status': 'confirmed' }) self.db.instance_update(context, instance_id, {'host': migration_ref['dest_compute'], }) diff --git a/nova/compute/manager.py b/nova/compute/manager.py index 4bab7081a..33fad50fd 100644 --- a/nova/compute/manager.py +++ b/nova/compute/manager.py @@ -388,8 +388,6 @@ class ComputeManager(manager.Manager): instance_ref = self.db.instance_get(context, instance_id) migration_ref = self.db.migration_get(context, migration_id) self.driver.destroy(instance_ref) - self.db.migration_update(context, migration_id, - { 'status': 'confirmed' }) @exception.wrap_exception @checks_instance_lock diff --git a/nova/db/api.py b/nova/db/api.py index 295d1a90a..ab871c67e 100644 --- a/nova/db/api.py +++ b/nova/db/api.py @@ -273,7 +273,8 @@ def migration_get(context, migration_id): def migration_get_by_instance_and_status(context, instance_id, status): """Finds a migration by the instance id its migrating""" - return IMPL.migration_get_by_instance(context, instance_id) + return IMPL.migration_get_by_instance_and_status(context, instance_id, + status) #################### -- cgit From 1ba8f07b9fb696ffa601f5d9104612505207d147 Mon Sep 17 00:00:00 2001 From: Ken Pepple Date: Wed, 16 Feb 2011 23:02:24 -0800 Subject: first crack at instance types docs --- doc/source/adminguide/managing.instance.types.rst | 79 +++++++++++++++++++++++ 1 file changed, 79 insertions(+) create mode 100644 doc/source/adminguide/managing.instance.types.rst diff --git a/doc/source/adminguide/managing.instance.types.rst b/doc/source/adminguide/managing.instance.types.rst new file mode 100644 index 000000000..ff4e2b087 --- /dev/null +++ b/doc/source/adminguide/managing.instance.types.rst @@ -0,0 +1,79 @@ + + Copyright 2010-2011 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. + +Managing Instance Types and Flavors +=================================== + +What are Instance Types or Flavors ? +------------------------------------ + +Instance types are the container descriptions (meta-data) about instances. In layman terms, this is the size of the instance (vCPUs, RAM, etc.) that you will be launching. In the EC2 API, these are called by names such as "m1.large" or "m1.tiny", while the OpenStack API terms these "flavors" with names like "" + +Flavors are simply the name for instance types used in the OpenStack API. In nova, these are equivalent terms, so when you create an instance type you are also creating a flavor. For the rest of this document, I will refer to these as instance types. + +In the current (Cactus) version of nova, instance types can only be created by the nova administrator through the nova-manage command. Future versions of nova (in concert with the OpenStack API or EC2 API), may expose this functionality directly to users. + +Basic Management +---------------- + +Instance types / flavor are managed through the nova-manage binary with +the "instance_type" command and an appropriate subcommand. Note that you can also use +the "flavor" command as a synonym for "instance_types". + +To see all currently active instance types, use the list subcommand:: + + $ nova-manage instance_type list + m1.medium: Memory: 4096MB, VCPUS: 2, Storage: 40GB, FlavorID: 3, Swap: 0GB, RXTX Quota: 0GB, RXTX Cap: 0MB + m1.large: Memory: 8192MB, VCPUS: 4, Storage: 80GB, FlavorID: 4, Swap: 0GB, RXTX Quota: 0GB, RXTX Cap: 0MB + m1.tiny: Memory: 512MB, VCPUS: 1, Storage: 0GB, FlavorID: 1, Swap: 0GB, RXTX Quota: 0GB, RXTX Cap: 0MB + m1.xlarge: Memory: 16384MB, VCPUS: 8, Storage: 160GB, FlavorID: 5, Swap: 0GB, RXTX Quota: 0GB, RXTX Cap: 0MB + m1.small: Memory: 2048MB, VCPUS: 1, Storage: 20GB, FlavorID: 2, Swap: 0GB, RXTX Quota: 0GB, RXTX Cap: 0MB + +By default, the list subcommand only shows active instance types. To see all instance types +(even those deleted), add the argument 1 after the list subcommand like so:: + + $ nova-manage instance_type list 1 + m1.medium: Memory: 4096MB, VCPUS: 2, Storage: 40GB, FlavorID: 3, Swap: 0GB, RXTX Quota: 0GB, RXTX Cap: 0MB + m1.large: Memory: 8192MB, VCPUS: 4, Storage: 80GB, FlavorID: 4, Swap: 0GB, RXTX Quota: 0GB, RXTX Cap: 0MB + m1.tiny: Memory: 512MB, VCPUS: 1, Storage: 0GB, FlavorID: 1, Swap: 0GB, RXTX Quota: 0GB, RXTX Cap: 0MB + m1.xlarge: Memory: 16384MB, VCPUS: 8, Storage: 160GB, FlavorID: 5, Swap: 0GB, RXTX Quota: 0GB, RXTX Cap: 0MB + m1.small: Memory: 2048MB, VCPUS: 1, Storage: 20GB, FlavorID: 2, Swap: 0GB, RXTX Quota: 0GB, RXTX Cap: 0MB + m1.deleted: Memory: 2048MB, VCPUS: 1, Storage: 20GB, FlavorID: 2, Swap: 0GB, RXTX Quota: 0GB, RXTX Cap: 0MB + +To create an instance type, use the "create" subcommand with the following positional arguments: + * memory (expressed in megabytes) + * vcpu(s) (integer) + * local storage (expressed in gigabytes) + * flavorid (unique integer) + * swap space (expressed in megabytes, defaults to zero, optional) + * RXTX quotas (expressed in gigabytes, defaults to zero, optional) + * RXTX cap (expressed in gigabytes, defaults to zero, optional) + +The following example creates an instance type named "m1.xxlarge":: + + # nova-manage instance_type create m1.xxlarge 32768 16 320 0 0 0 + m1.xxlarge created + +To delete an instance type, use the "delete" subcommand and specify the name:: + + # nova-manage instance_type delete m1.xxlarge + m1.xxlarge deleted + +Please note that the "delete" command only marks the instance type as +inactive in the database; it does not actually remove the instance type. This is done +to preserve the instance type definition for long running instances (which may not +terminate for months or years). -- cgit From 9d056b6fadcefed9ef9573bd89125b00af5e2726 Mon Sep 17 00:00:00 2001 From: Josh Kearney Date: Thu, 17 Feb 2011 10:50:49 -0600 Subject: More testing --- nova/api/openstack/__init__.py | 1 + nova/api/openstack/flavors.py | 14 ++++++++++++-- nova/tests/api/openstack/test_flavors.py | 6 ++++-- 3 files changed, 17 insertions(+), 4 deletions(-) diff --git a/nova/api/openstack/__init__.py b/nova/api/openstack/__init__.py index 056c7dd27..1fafebe37 100644 --- a/nova/api/openstack/__init__.py +++ b/nova/api/openstack/__init__.py @@ -73,6 +73,7 @@ class APIRouter(wsgi.Router): server_members = {'action': 'POST'} if FLAGS.allow_admin_api: LOG.debug(_("Including admin operations in API.")) + server_members['pause'] = 'POST' server_members['unpause'] = 'POST' server_members["diagnostics"] = "GET" diff --git a/nova/api/openstack/flavors.py b/nova/api/openstack/flavors.py index 215f0b8a6..a3a664506 100644 --- a/nova/api/openstack/flavors.py +++ b/nova/api/openstack/flavors.py @@ -58,11 +58,21 @@ class Controller(wsgi.Controller): def create(self, req): """Create a flavor.""" + instance_types.create( + name, + memory, + vcpus, + local_gb, + flavor_id, + swap, + rxtx_quota, + rxtx_cap) print "CREATE! %s" % req - def delete(self, req, id): + def delete(self, req, name): """Delete a flavor.""" - print "DELETE! %s %s" % (req, id) + instance_type.destroy(name) + print "DELETE! %s %s" % (req, name) def _all_ids(self): """Return the list of all flavorids.""" diff --git a/nova/tests/api/openstack/test_flavors.py b/nova/tests/api/openstack/test_flavors.py index 7bfc46e0b..209ace0f8 100644 --- a/nova/tests/api/openstack/test_flavors.py +++ b/nova/tests/api/openstack/test_flavors.py @@ -46,12 +46,14 @@ class FlavorsTest(unittest.TestCase): self.assertEqual(res.status_int, 200) def test_create_flavor(self): - req = webob.Request.blank("/v1.0/flavors/create/test") + req = webob.Request.blank("/v1.0/flavors") + req.method = "POST" res = req.get_response(fakes.wsgi_app()) self.assertEqual(res.status_int, 200) def test_delete_flavor(self): - req = webob.Request.blank("/v1.0/flavors/delete/test") + req = webob.Request.blank("/v1.0/flavors/1") + req.method = "DELETE" res = req.get_response(fakes.wsgi_app()) self.assertEqual(res.status_int, 200) -- cgit From e67927c181a1f24df35a6df5663e397e260979cf Mon Sep 17 00:00:00 2001 From: Cerberus Date: Thu, 17 Feb 2011 13:28:39 -0600 Subject: Foo --- nova/api/openstack/servers.py | 2 +- nova/compute/manager.py | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/nova/api/openstack/servers.py b/nova/api/openstack/servers.py index 2fc105d07..5f369463d 100644 --- a/nova/api/openstack/servers.py +++ b/nova/api/openstack/servers.py @@ -216,7 +216,7 @@ class Controller(wsgi.Controller): def _action_revert_resize(self, input_dict, req, id): try: - self.compute_api.confirm_resize(req.environ['nova.context'], id) + self.compute_api.revert_resize(req.environ['nova.context'], id) except Exception, e: LOG.exception(_("Error in revert-resize %s"), e) return faults.Fault(exc.HTTPBadRequest(e)) diff --git a/nova/compute/manager.py b/nova/compute/manager.py index 33fad50fd..4e6532ef4 100644 --- a/nova/compute/manager.py +++ b/nova/compute/manager.py @@ -398,7 +398,8 @@ class ComputeManager(manager.Manager): instance_ref = self.db.instance_get(context, instance_id) migration_ref = self.db.migration_get(context, migration_id) - if migration_ref['source_compute'] == instance_ref['host']: + #TODO(mdietz): we may want to split these into separate methods. + if migration_ref['source_compute'] == FLAGS.host: self.driver.power_on(instance_ref) self.db.migration_update(context, migration_id, { 'status': 'reverted' }) -- cgit From b1fe9a64143505235eb2e3dbe6a6c0966a85ae76 Mon Sep 17 00:00:00 2001 From: Cerberus Date: Thu, 17 Feb 2011 14:10:08 -0600 Subject: Stop blowing away the ramdisk --- nova/compute/manager.py | 2 +- nova/virt/xenapi/vmops.py | 29 ++++++++++++++++------------- 2 files changed, 17 insertions(+), 14 deletions(-) diff --git a/nova/compute/manager.py b/nova/compute/manager.py index 4e6532ef4..e81ee3148 100644 --- a/nova/compute/manager.py +++ b/nova/compute/manager.py @@ -408,7 +408,7 @@ class ComputeManager(manager.Manager): topic = self.db.queue_get_for(context, FLAGS.compute_topic, instance_ref['host']) rpc.cast(context, topic, - { 'method': 'resize_instance', + { 'method': 'revert_resize', 'args': { 'migration_id': migration_ref['id'], 'instance_id': instance_id, diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py index 13c13d0f6..17b91d27c 100644 --- a/nova/virt/xenapi/vmops.py +++ b/nova/virt/xenapi/vmops.py @@ -234,7 +234,8 @@ class VMOps(object): return self def __exit__(self, type, value, traceback): - self.virt._destroy(self.instance, self.vm_ref, shutdown=False) + self.virt._destroy(self.instance, self.vm_ref, shutdown=False, + destroy_kernel_ramdisk=False) #TODO(sirp): Add quiesce and VSS locking support when Windows support # is added @@ -393,7 +394,7 @@ class VMOps(object): except self.XenAPI.Failure, exc: LOG.exception(exc) - def _destroy_vm(self, instance, vm): + def _destroy_vm(self, instance, vm, destroy_kernel_ramdisk): """Destroys a VM record """ try: kernel = None @@ -402,16 +403,18 @@ class VMOps(object): (kernel, ramdisk) = VMHelper.lookup_kernel_ramdisk( self._session, vm) task1 = self._session.call_xenapi('Async.VM.destroy', vm) - LOG.debug(_("Removing kernel/ramdisk files")) - fn = "remove_kernel_ramdisk" - args = {} - if kernel: - args['kernel-file'] = kernel - if ramdisk: - args['ramdisk-file'] = ramdisk - task2 = self._session.async_call_plugin('glance', fn, args) + if destroy_kernel_ramdisk: + LOG.debug(_("Removing kernel/ramdisk files")) + fn = "remove_kernel_ramdisk" + args = {} + if kernel: + args['kernel-file'] = kernel + if ramdisk: + args['ramdisk-file'] = ramdisk + task2 = self._session.async_call_plugin('glance', fn, args) self._session.wait_for_task(instance.id, task1) - self._session.wait_for_task(instance.id, task2) + if destroy_kernel_ramdisk: + self._session.wait_for_task(instance.id, task2) LOG.debug(_("kernel/ramdisk files removed")) except self.XenAPI.Failure, exc: LOG.exception(exc) @@ -426,7 +429,7 @@ class VMOps(object): vm = VMHelper.lookup(self._session, instance.name) return self._destroy(instance, vm, shutdown=True) - def _destroy(self, instance, vm, shutdown=True): + def _destroy(self, instance, vm, shutdown=True, destroy_kernel_ramdisk=True): """ Destroys VM instance by performing: @@ -443,7 +446,7 @@ class VMOps(object): self._shutdown(instance, vm) self._destroy_vdis(instance, vm) - self._destroy_vm(instance, vm) + self._destroy_vm(instance, vm, destroy_kernel_ramdisk) def _wait_with_callback(self, instance_id, task, callback): ret = None -- cgit From 3dd6e369c0aa2e3092eaa32a6b04cbba712ba5ad Mon Sep 17 00:00:00 2001 From: Cerberus Date: Thu, 17 Feb 2011 14:23:20 -0600 Subject: Move the ramdisk logging stuff --- nova/virt/xenapi/vmops.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py index 17b91d27c..f7e434300 100644 --- a/nova/virt/xenapi/vmops.py +++ b/nova/virt/xenapi/vmops.py @@ -412,10 +412,10 @@ class VMOps(object): if ramdisk: args['ramdisk-file'] = ramdisk task2 = self._session.async_call_plugin('glance', fn, args) + LOG.debug(_("kernel/ramdisk files removed")) self._session.wait_for_task(instance.id, task1) if destroy_kernel_ramdisk: self._session.wait_for_task(instance.id, task2) - LOG.debug(_("kernel/ramdisk files removed")) except self.XenAPI.Failure, exc: LOG.exception(exc) -- cgit From aa53c9476ed37f0a1359413d4a710eb08c997b06 Mon Sep 17 00:00:00 2001 From: Josh Kearney Date: Thu, 17 Feb 2011 14:42:01 -0600 Subject: Finished flavor OS API stubs --- nova/api/openstack/flavors.py | 28 +++++++++++++++------------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/nova/api/openstack/flavors.py b/nova/api/openstack/flavors.py index a3a664506..375e12b18 100644 --- a/nova/api/openstack/flavors.py +++ b/nova/api/openstack/flavors.py @@ -58,21 +58,23 @@ class Controller(wsgi.Controller): def create(self, req): """Create a flavor.""" - instance_types.create( - name, - memory, - vcpus, - local_gb, - flavor_id, - swap, - rxtx_quota, - rxtx_cap) - print "CREATE! %s" % req + #TODO(jk0): Finish this later + #instance_types.create( + # name, + # memory, + # vcpus, + # local_gb, + # flavor_id, + # swap, + # rxtx_quota, + # rxtx_cap) + return "CREATE! %s" % req - def delete(self, req, name): + def delete(self, req, id): """Delete a flavor.""" - instance_type.destroy(name) - print "DELETE! %s %s" % (req, name) + #TODO(jk0): Finish this later + #instance_type.destroy(name) + return "DELETE! %s %s" % (req, id) def _all_ids(self): """Return the list of all flavorids.""" -- cgit From 8da339d53b4039c3a8e5e8a15ccf1434eeda5fa2 Mon Sep 17 00:00:00 2001 From: Josh Kearney Date: Thu, 17 Feb 2011 15:05:19 -0600 Subject: Fixed unit test --- nova/virt/xenapi/vm_utils.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/nova/virt/xenapi/vm_utils.py b/nova/virt/xenapi/vm_utils.py index 339cecd40..7031f2f82 100644 --- a/nova/virt/xenapi/vm_utils.py +++ b/nova/virt/xenapi/vm_utils.py @@ -141,7 +141,8 @@ class VMHelper(HelperBase): @classmethod def ensure_free_mem(cls, session, instance): - instance_type = instance_types.INSTANCE_TYPES[instance.instance_type] + instance_type = instance_types.get_instance_type( + instance.instance_type) mem = long(instance_type['memory_mb']) * 1024 * 1024 #get free memory from host host = session.get_xenapi_host() -- cgit From 273119957fb3f6cfa72d4357054f6ad1743704e8 Mon Sep 17 00:00:00 2001 From: Ken Pepple Date: Thu, 17 Feb 2011 13:49:36 -0800 Subject: moved 003_cactus.py migration file to 004_add_instance_types.py to avoid naming collision with new trunk migration --- .../sqlalchemy/migrate_repo/versions/003_cactus.py | 86 ---------------------- .../versions/004_add_instance_types.py | 86 ++++++++++++++++++++++ 2 files changed, 86 insertions(+), 86 deletions(-) delete mode 100644 nova/db/sqlalchemy/migrate_repo/versions/003_cactus.py create mode 100644 nova/db/sqlalchemy/migrate_repo/versions/004_add_instance_types.py diff --git a/nova/db/sqlalchemy/migrate_repo/versions/003_cactus.py b/nova/db/sqlalchemy/migrate_repo/versions/003_cactus.py deleted file mode 100644 index fec191214..000000000 --- a/nova/db/sqlalchemy/migrate_repo/versions/003_cactus.py +++ /dev/null @@ -1,86 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# 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. - -from sqlalchemy import * -from migrate import * - -from nova import api -from nova import db -from nova import log as logging - -import datetime - -meta = MetaData() - - -# -# New Tables -# -instance_types = Table('instance_types', meta, - Column('created_at', DateTime(timezone=False)), - Column('updated_at', DateTime(timezone=False)), - Column('deleted_at', DateTime(timezone=False)), - Column('deleted', Boolean(create_constraint=True, name=None)), - Column('name', - String(length=255, convert_unicode=False, assert_unicode=None, - unicode_error=None, _warn_on_bytestring=False), - unique=True), - Column('id', Integer(), primary_key=True, nullable=False), - Column('memory_mb', Integer(), nullable=False), - Column('vcpus', Integer(), nullable=False), - Column('local_gb', Integer(), nullable=False), - Column('flavorid', Integer(), nullable=False, unique=True), - Column('swap', Integer(), nullable=False, default=0), - Column('rxtx_quota', Integer(), nullable=False, default=0), - Column('rxtx_cap', Integer(), nullable=False, default=0)) - - -def upgrade(migrate_engine): - # Upgrade operations go here - # Don't create your own engine; bind migrate_engine - # to your metadata - meta.bind = migrate_engine - try: - instance_types.create() - except Exception: - logging.info(repr(table)) - logging.exception('Exception while creating instance_types table') - raise - - # Here are the old static instance types - INSTANCE_TYPES = { - 'm1.tiny': dict(memory_mb=512, vcpus=1, local_gb=0, flavorid=1), - 'm1.small': dict(memory_mb=2048, vcpus=1, local_gb=20, flavorid=2), - 'm1.medium': dict(memory_mb=4096, vcpus=2, local_gb=40, flavorid=3), - 'm1.large': dict(memory_mb=8192, vcpus=4, local_gb=80, flavorid=4), - 'm1.xlarge': dict(memory_mb=16384, vcpus=8, local_gb=160, flavorid=5)} - try: - i = instance_types.insert() - for name, values in INSTANCE_TYPES.iteritems(): - # FIXME(kpepple) should we be seeding created_at / updated_at ? - # now = datetime.datatime.utcnow() - i.execute({'name': name, 'memory_mb': values["memory_mb"], - 'vcpus': values["vcpus"], 'deleted': 0, - 'local_gb': values["local_gb"], - 'flavorid': values["flavorid"]}) - except Exception: - logging.info(repr(table)) - logging.exception('Exception while seeding instance_types table') - raise - - -def downgrade(migrate_engine): - # Operations to reverse the above upgrade go here. - for table in (instance_types): - table.drop() diff --git a/nova/db/sqlalchemy/migrate_repo/versions/004_add_instance_types.py b/nova/db/sqlalchemy/migrate_repo/versions/004_add_instance_types.py new file mode 100644 index 000000000..fec191214 --- /dev/null +++ b/nova/db/sqlalchemy/migrate_repo/versions/004_add_instance_types.py @@ -0,0 +1,86 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# 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. + +from sqlalchemy import * +from migrate import * + +from nova import api +from nova import db +from nova import log as logging + +import datetime + +meta = MetaData() + + +# +# New Tables +# +instance_types = Table('instance_types', meta, + Column('created_at', DateTime(timezone=False)), + Column('updated_at', DateTime(timezone=False)), + Column('deleted_at', DateTime(timezone=False)), + Column('deleted', Boolean(create_constraint=True, name=None)), + Column('name', + String(length=255, convert_unicode=False, assert_unicode=None, + unicode_error=None, _warn_on_bytestring=False), + unique=True), + Column('id', Integer(), primary_key=True, nullable=False), + Column('memory_mb', Integer(), nullable=False), + Column('vcpus', Integer(), nullable=False), + Column('local_gb', Integer(), nullable=False), + Column('flavorid', Integer(), nullable=False, unique=True), + Column('swap', Integer(), nullable=False, default=0), + Column('rxtx_quota', Integer(), nullable=False, default=0), + Column('rxtx_cap', Integer(), nullable=False, default=0)) + + +def upgrade(migrate_engine): + # Upgrade operations go here + # Don't create your own engine; bind migrate_engine + # to your metadata + meta.bind = migrate_engine + try: + instance_types.create() + except Exception: + logging.info(repr(table)) + logging.exception('Exception while creating instance_types table') + raise + + # Here are the old static instance types + INSTANCE_TYPES = { + 'm1.tiny': dict(memory_mb=512, vcpus=1, local_gb=0, flavorid=1), + 'm1.small': dict(memory_mb=2048, vcpus=1, local_gb=20, flavorid=2), + 'm1.medium': dict(memory_mb=4096, vcpus=2, local_gb=40, flavorid=3), + 'm1.large': dict(memory_mb=8192, vcpus=4, local_gb=80, flavorid=4), + 'm1.xlarge': dict(memory_mb=16384, vcpus=8, local_gb=160, flavorid=5)} + try: + i = instance_types.insert() + for name, values in INSTANCE_TYPES.iteritems(): + # FIXME(kpepple) should we be seeding created_at / updated_at ? + # now = datetime.datatime.utcnow() + i.execute({'name': name, 'memory_mb': values["memory_mb"], + 'vcpus': values["vcpus"], 'deleted': 0, + 'local_gb': values["local_gb"], + 'flavorid': values["flavorid"]}) + except Exception: + logging.info(repr(table)) + logging.exception('Exception while seeding instance_types table') + raise + + +def downgrade(migrate_engine): + # Operations to reverse the above upgrade go here. + for table in (instance_types): + table.drop() -- cgit From 3f3dddee0245cb143004dfb8c20204c511bec658 Mon Sep 17 00:00:00 2001 From: Cerberus Date: Thu, 17 Feb 2011 16:52:31 -0600 Subject: a few changes and a bunch of unit tests --- nova/api/openstack/servers.py | 16 ++-- .../sqlalchemy/migrate_repo/versions/003_cactus.py | 60 ------------- .../versions/004_add_instance_migrations.py | 60 +++++++++++++ nova/tests/api/openstack/common.py | 30 +++++++ nova/tests/api/openstack/test_servers.py | 98 +++++++++++++++++++++- 5 files changed, 197 insertions(+), 67 deletions(-) delete mode 100644 nova/db/sqlalchemy/migrate_repo/versions/003_cactus.py create mode 100644 nova/db/sqlalchemy/migrate_repo/versions/004_add_instance_migrations.py create mode 100644 nova/tests/api/openstack/common.py diff --git a/nova/api/openstack/servers.py b/nova/api/openstack/servers.py index fd6b10d5b..a719f5e15 100644 --- a/nova/api/openstack/servers.py +++ b/nova/api/openstack/servers.py @@ -227,7 +227,7 @@ class Controller(wsgi.Controller): self.compute_api.confirm_resize(req.environ['nova.context'], id) except Exception, e: LOG.exception(_("Error in confirm-resize %s"), e) - return faults.Fault(exc.HTTPBadRequest(e)) + return faults.Fault(exc.HTTPBadRequest()) return exc.HTTPNoContent() def _action_revert_resize(self, input_dict, req, id): @@ -235,7 +235,7 @@ class Controller(wsgi.Controller): self.compute_api.revert_resize(req.environ['nova.context'], id) except Exception, e: LOG.exception(_("Error in revert-resize %s"), e) - return faults.Fault(exc.HTTPBadRequest(e)) + return faults.Fault(exc.HTTPBadRequest()) return exc.HTTPAccepted() def _action_rebuild(self, input_dict, req, id): @@ -244,12 +244,16 @@ class Controller(wsgi.Controller): def _action_resize(self, input_dict, req, id): """ Resizes a given instance to the flavor size requested """ try: - flavor_id = input_dict['resize']['flavorId'] - self.compute_api.resize(req.environ['nova.context'], id, - flavor_id) + if 'resize' in input_dict and 'flavorId' in input_dict['resize']: + flavor_id = input_dict['resize']['flavorId'] + self.compute_api.resize(req.environ['nova.context'], id, + flavor_id) + else: + LOG.exception(_("Missing arguments for resize")) + return faults.Fault(exc.HTTPUnprocessableEntity()) except Exception, e: LOG.exception(_("Error in resize %s"), e) - return faults.Fault(exc.HTTPUnprocessableEntity(e)) + return faults.Fault(exc.HTTPBadRequest()) return faults.Fault(exc.HTTPAccepted()) diff --git a/nova/db/sqlalchemy/migrate_repo/versions/003_cactus.py b/nova/db/sqlalchemy/migrate_repo/versions/003_cactus.py deleted file mode 100644 index 4aab5bdc6..000000000 --- a/nova/db/sqlalchemy/migrate_repo/versions/003_cactus.py +++ /dev/null @@ -1,60 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright 2010 OpenStack LLC. -# 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.from sqlalchemy import * - -from sqlalchemy import * -from migrate import * - -from nova import log as logging - - -meta = MetaData() - -# Just for the ForeignKey and column creation to succeed, these are not the -# actual definitions of instances or services. -instances = Table('instances', meta, - Column('id', Integer(), primary_key=True, nullable=False), - ) - -# -# New Tables -# - -migrations = Table('migrations', meta, - Column('created_at', DateTime(timezone=False)), - Column('updated_at', DateTime(timezone=False)), - Column('deleted_at', DateTime(timezone=False)), - Column('deleted', Boolean(create_constraint=True, name=None)), - Column('id', Integer(), primary_key=True, nullable=False), - Column('source_compute', String(255)), - Column('dest_compute', String(255)), - Column('dest_host', String(255)), - Column('instance_id', Integer, ForeignKey('instances.id'), - nullable=True), - Column('status', String(255)) - ) - -def upgrade(migrate_engine): - # Upgrade operations go here. Don't create your own engine; - # bind migrate_engine to your metadata - meta.bind = migrate_engine - for table in (migrations, ): - try: - table.create() - except Exception: - logging.info(repr(table)) - logging.exception('Exception while creating table') - raise diff --git a/nova/db/sqlalchemy/migrate_repo/versions/004_add_instance_migrations.py b/nova/db/sqlalchemy/migrate_repo/versions/004_add_instance_migrations.py new file mode 100644 index 000000000..4aab5bdc6 --- /dev/null +++ b/nova/db/sqlalchemy/migrate_repo/versions/004_add_instance_migrations.py @@ -0,0 +1,60 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright 2010 OpenStack LLC. +# 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.from sqlalchemy import * + +from sqlalchemy import * +from migrate import * + +from nova import log as logging + + +meta = MetaData() + +# Just for the ForeignKey and column creation to succeed, these are not the +# actual definitions of instances or services. +instances = Table('instances', meta, + Column('id', Integer(), primary_key=True, nullable=False), + ) + +# +# New Tables +# + +migrations = Table('migrations', meta, + Column('created_at', DateTime(timezone=False)), + Column('updated_at', DateTime(timezone=False)), + Column('deleted_at', DateTime(timezone=False)), + Column('deleted', Boolean(create_constraint=True, name=None)), + Column('id', Integer(), primary_key=True, nullable=False), + Column('source_compute', String(255)), + Column('dest_compute', String(255)), + Column('dest_host', String(255)), + Column('instance_id', Integer, ForeignKey('instances.id'), + nullable=True), + Column('status', String(255)) + ) + +def upgrade(migrate_engine): + # Upgrade operations go here. Don't create your own engine; + # bind migrate_engine to your metadata + meta.bind = migrate_engine + for table in (migrations, ): + try: + table.create() + except Exception: + logging.info(repr(table)) + logging.exception('Exception while creating table') + raise diff --git a/nova/tests/api/openstack/common.py b/nova/tests/api/openstack/common.py new file mode 100644 index 000000000..b55d3087b --- /dev/null +++ b/nova/tests/api/openstack/common.py @@ -0,0 +1,30 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright 2011 OpenStack LLC. +# 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. + +import json + +import webob + +def webob_factory(url): + base_url = url + def web_request(url, method, body=None): + req = webob.Request.blank("%s%s" % (base_url, url)) + req.method = method + req.body = json.dumps(body) + return req + return web_request + diff --git a/nova/tests/api/openstack/test_servers.py b/nova/tests/api/openstack/test_servers.py index a7be0796e..878b62f85 100644 --- a/nova/tests/api/openstack/test_servers.py +++ b/nova/tests/api/openstack/test_servers.py @@ -1,6 +1,6 @@ # vim: tabstop=4 shiftwidth=4 softtabstop=4 -# Copyright 2010 OpenStack LLC. +# Copyright 2010-2011 OpenStack LLC. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may @@ -29,6 +29,7 @@ from nova.api.openstack import servers import nova.db.api from nova.db.sqlalchemy.models import Instance import nova.rpc +from nova.tests.api.openstack import common from nova.tests.api.openstack import fakes @@ -138,6 +139,8 @@ class ServersTest(unittest.TestCase): self.stubs.Set(nova.compute.API, "get_actions", fake_compute_api) self.allow_admin = FLAGS.allow_admin_api + self.webreq = common.webob_factor('/v1.0/servers') + def tearDown(self): self.stubs.UnsetAll() FLAGS.allow_admin_api = self.allow_admin @@ -411,6 +414,99 @@ class ServersTest(unittest.TestCase): self.assertEqual(res.status, '202 Accepted') self.assertEqual(self.server_delete_called, True) + def test_resize_server(self): + req = self.webreq('/1/action', 'POST', dict(resize={flavorId=3}})) + res = req.get_response(fakes.wsgi_app()) + + self.resize_called = False + def resize_mock(context, inst_id, flavor): + self.resize_called = True + + self.stubs.Set(nova.compute.api, 'resize_instance', resize_mock) + self.assertEqual(res.status_int, 202) + self.assertEqual(self.resize_called, True) + + def test_resize_bad_param_fails(self): + req = self.webreq('/1/action', 'POST', dict('ferp')) + res = req.get_response(fakes.wsgi_app()) + + self.resize_called = False + def resize_mock(context, inst_id, flavor): + self.resize_called = True + + self.stubs.Set(nova.compute.api, 'resize_instance', resize_mock) + self.assertEqual(res.status_int, 422) + self.assertEqual(self.resize_called, False) + + def test_resize_bad_flavor_fails(self): + req = self.webreq('/1/action', 'POST', dict(resize={derp=3})) + res = req.get_response(fakes.wsgi_app()) + + self.resize_called = False + def resize_mock(context, inst_id, flavor): + self.resize_called = True + + self.stubs.Set(nova.compute.api, 'resize_instance', resize_mock) + self.assertEqual(res.status_int, 422) + self.assertEqual(self.resize_called, False) + + def test_resize_raises_fails(self): + req = self.webreq('/1/action', 'POST', dict(resize={flavorId=3})) + res = req.get_response(fakes.wsgi_app()) + + def resize_mock(context, inst_id, flavor): + raise Exception, 'hurr durr' + + self.stubs.Set(nova.compute.api, 'resize_instance', resize_mock) + self.assertEqual(res.status_int, 500) + + def test_confirm_resize_server(self): + req = self.webreq('/1/action', 'POST', dict(confirmResize=None)) + res = req.get_response(fakes.wsgi_app()) + + self.resize_called = False + def confirm_resize_mock(context, inst_id): + self.resize_called = True + + self.stubs.Set(nova.compute.api, 'confirm_resize', + confirm_resize_mock) + self.assertEqual(res.status_int, 202) + self.assertEqual(self.resize_called, True) + + def test_confirm_resize_server_fails(self): + req = self.webreq('/1/action', 'POST', dict(confirmResize=None)) + res = req.get_response(fakes.wsgi_app()) + + def confirm_resize_mock(context, inst_id): + raise Exception, 'hurr durr' + + self.stubs.Set(nova.compute.api, 'confirm_resize', + confirm_resize_mock) + self.assertEqual(res.status_int, 500) + + def test_revert_resize_server(self): + req = self.webreq('/1/action', 'POST', dict(revertResize=None)) + res = req.get_response(fakes.wsgi_app()) + + self.resize_called = False + def revert_resize_mock(context, inst_id): + self.resize_called = True + + self.stubs.Set(nova.compute.api, 'revert_resize', + confirm_resize_mock) + self.assertEqual(res.status_int, 202) + self.assertEqual(self.resize_called, True) + + def test_revert_resize_server_fails(self): + req = self.webreq('/1/action', 'POST', dict(confirmResize=None)) + res = req.get_response(fakes.wsgi_app()) + + def revert_resize_mock(context, inst_id): + raise Exception, 'hurr durr' + + self.stubs.Set(nova.compute.api, 'revert_resize', + confirm_resize_mock) + self.assertEqual(res.status_int, 500) if __name__ == "__main__": unittest.main() -- cgit From 88aa545b53d96c25da01218c79e8be8c1ae3370f Mon Sep 17 00:00:00 2001 From: "matt.dietz@rackspace.com" <> Date: Thu, 17 Feb 2011 23:55:56 +0000 Subject: Test changes --- nova/tests/api/openstack/test_servers.py | 82 +++++++++++++++----------------- 1 file changed, 39 insertions(+), 43 deletions(-) diff --git a/nova/tests/api/openstack/test_servers.py b/nova/tests/api/openstack/test_servers.py index 878b62f85..665551c55 100644 --- a/nova/tests/api/openstack/test_servers.py +++ b/nova/tests/api/openstack/test_servers.py @@ -26,6 +26,7 @@ from nova import db from nova import flags import nova.api.openstack from nova.api.openstack import servers +import nova.compute.api import nova.db.api from nova.db.sqlalchemy.models import Instance import nova.rpc @@ -139,7 +140,7 @@ class ServersTest(unittest.TestCase): self.stubs.Set(nova.compute.API, "get_actions", fake_compute_api) self.allow_admin = FLAGS.allow_admin_api - self.webreq = common.webob_factor('/v1.0/servers') + self.webreq = common.webob_factory('/v1.0/servers') def tearDown(self): self.stubs.UnsetAll() @@ -415,98 +416,93 @@ class ServersTest(unittest.TestCase): self.assertEqual(self.server_delete_called, True) def test_resize_server(self): - req = self.webreq('/1/action', 'POST', dict(resize={flavorId=3}})) - res = req.get_response(fakes.wsgi_app()) + req = self.webreq('/1/action', 'POST', dict(resize=dict(flavorId=3))) self.resize_called = False - def resize_mock(context, inst_id, flavor): + def resize_mock(*args): self.resize_called = True - self.stubs.Set(nova.compute.api, 'resize_instance', resize_mock) + self.stubs.Set(nova.compute.api.API, 'resize', resize_mock) + + res = req.get_response(fakes.wsgi_app()) self.assertEqual(res.status_int, 202) self.assertEqual(self.resize_called, True) - def test_resize_bad_param_fails(self): - req = self.webreq('/1/action', 'POST', dict('ferp')) - res = req.get_response(fakes.wsgi_app()) + def test_resize_bad_flavor_fails(self): + req = self.webreq('/1/action', 'POST', dict(resize=dict(derp=3))) self.resize_called = False - def resize_mock(context, inst_id, flavor): + def resize_mock(*args): self.resize_called = True - self.stubs.Set(nova.compute.api, 'resize_instance', resize_mock) - self.assertEqual(res.status_int, 422) - self.assertEqual(self.resize_called, False) + self.stubs.Set(nova.compute.api.API, 'resize', resize_mock) - def test_resize_bad_flavor_fails(self): - req = self.webreq('/1/action', 'POST', dict(resize={derp=3})) res = req.get_response(fakes.wsgi_app()) - - self.resize_called = False - def resize_mock(context, inst_id, flavor): - self.resize_called = True - - self.stubs.Set(nova.compute.api, 'resize_instance', resize_mock) self.assertEqual(res.status_int, 422) self.assertEqual(self.resize_called, False) def test_resize_raises_fails(self): - req = self.webreq('/1/action', 'POST', dict(resize={flavorId=3})) - res = req.get_response(fakes.wsgi_app()) + req = self.webreq('/1/action', 'POST', dict(resize=dict(flavorId=3))) - def resize_mock(context, inst_id, flavor): + def resize_mock(*args): raise Exception, 'hurr durr' - self.stubs.Set(nova.compute.api, 'resize_instance', resize_mock) - self.assertEqual(res.status_int, 500) + self.stubs.Set(nova.compute.api.API, 'resize', resize_mock) + + res = req.get_response(fakes.wsgi_app()) + self.assertEqual(res.status_int, 400) def test_confirm_resize_server(self): req = self.webreq('/1/action', 'POST', dict(confirmResize=None)) - res = req.get_response(fakes.wsgi_app()) self.resize_called = False - def confirm_resize_mock(context, inst_id): + def confirm_resize_mock(*args): self.resize_called = True - self.stubs.Set(nova.compute.api, 'confirm_resize', + self.stubs.Set(nova.compute.api.API, 'confirm_resize', confirm_resize_mock) - self.assertEqual(res.status_int, 202) + + res = req.get_response(fakes.wsgi_app()) + self.assertEqual(res.status_int, 204) self.assertEqual(self.resize_called, True) def test_confirm_resize_server_fails(self): req = self.webreq('/1/action', 'POST', dict(confirmResize=None)) - res = req.get_response(fakes.wsgi_app()) - def confirm_resize_mock(context, inst_id): + def confirm_resize_mock(*args): raise Exception, 'hurr durr' - self.stubs.Set(nova.compute.api, 'confirm_resize', + self.stubs.Set(nova.compute.api.API, 'confirm_resize', confirm_resize_mock) - self.assertEqual(res.status_int, 500) + + res = req.get_response(fakes.wsgi_app()) + self.assertEqual(res.status_int, 400) def test_revert_resize_server(self): req = self.webreq('/1/action', 'POST', dict(revertResize=None)) - res = req.get_response(fakes.wsgi_app()) self.resize_called = False - def revert_resize_mock(context, inst_id): + def revert_resize_mock(*args): self.resize_called = True - self.stubs.Set(nova.compute.api, 'revert_resize', - confirm_resize_mock) + self.stubs.Set(nova.compute.api.API, 'revert_resize', + revert_resize_mock) + + res = req.get_response(fakes.wsgi_app()) self.assertEqual(res.status_int, 202) self.assertEqual(self.resize_called, True) def test_revert_resize_server_fails(self): - req = self.webreq('/1/action', 'POST', dict(confirmResize=None)) - res = req.get_response(fakes.wsgi_app()) + req = self.webreq('/1/action', 'POST', dict(revertResize=None)) - def revert_resize_mock(context, inst_id): + def revert_resize_mock(*args): raise Exception, 'hurr durr' - self.stubs.Set(nova.compute.api, 'revert_resize', - confirm_resize_mock) - self.assertEqual(res.status_int, 500) + self.stubs.Set(nova.compute.api.API, 'revert_resize', + revert_resize_mock) + + res = req.get_response(fakes.wsgi_app()) + self.assertEqual(res.status_int, 400) if __name__ == "__main__": unittest.main() -- cgit From 9991f23957c07493d503c9667a6920e1235ef8a1 Mon Sep 17 00:00:00 2001 From: Ken Pepple Date: Thu, 17 Feb 2011 16:07:00 -0800 Subject: completed doc and added --purge option to instance type delete --- bin/nova-manage | 11 ++++++++--- doc/source/adminguide/managing.instance.types.rst | 10 +++++++--- 2 files changed, 15 insertions(+), 6 deletions(-) diff --git a/bin/nova-manage b/bin/nova-manage index 21de11474..3318a593e 100755 --- a/bin/nova-manage +++ b/bin/nova-manage @@ -713,16 +713,21 @@ class InstanceTypeCommands(object): else: print "%s created" % name - def delete(self, name): + def delete(self, name, purge=None): """Marks instance types / flavors as deleted arguments: name""" try: - instance_types.destroy(name) + if purge == "--purge": + instance_types.purge(name) + verb = "deleted" + else: + instance_types.destroy(name) + verb = "purged" except exception.ApiError: print "Valid instance type name is required" sys.exit(1) else: - print "%s deleted" % name + print "%s %s" % (name, verb) def list(self, name=None): """Lists all or specific instance types / flavors diff --git a/doc/source/adminguide/managing.instance.types.rst b/doc/source/adminguide/managing.instance.types.rst index ff4e2b087..30a47e47d 100644 --- a/doc/source/adminguide/managing.instance.types.rst +++ b/doc/source/adminguide/managing.instance.types.rst @@ -36,7 +36,7 @@ the "flavor" command as a synonym for "instance_types". To see all currently active instance types, use the list subcommand:: - $ nova-manage instance_type list + # nova-manage instance_type list m1.medium: Memory: 4096MB, VCPUS: 2, Storage: 40GB, FlavorID: 3, Swap: 0GB, RXTX Quota: 0GB, RXTX Cap: 0MB m1.large: Memory: 8192MB, VCPUS: 4, Storage: 80GB, FlavorID: 4, Swap: 0GB, RXTX Quota: 0GB, RXTX Cap: 0MB m1.tiny: Memory: 512MB, VCPUS: 1, Storage: 0GB, FlavorID: 1, Swap: 0GB, RXTX Quota: 0GB, RXTX Cap: 0MB @@ -46,7 +46,7 @@ To see all currently active instance types, use the list subcommand:: By default, the list subcommand only shows active instance types. To see all instance types (even those deleted), add the argument 1 after the list subcommand like so:: - $ nova-manage instance_type list 1 + # nova-manage instance_type list 1 m1.medium: Memory: 4096MB, VCPUS: 2, Storage: 40GB, FlavorID: 3, Swap: 0GB, RXTX Quota: 0GB, RXTX Cap: 0MB m1.large: Memory: 8192MB, VCPUS: 4, Storage: 80GB, FlavorID: 4, Swap: 0GB, RXTX Quota: 0GB, RXTX Cap: 0MB m1.tiny: Memory: 512MB, VCPUS: 1, Storage: 0GB, FlavorID: 1, Swap: 0GB, RXTX Quota: 0GB, RXTX Cap: 0MB @@ -76,4 +76,8 @@ To delete an instance type, use the "delete" subcommand and specify the name:: Please note that the "delete" command only marks the instance type as inactive in the database; it does not actually remove the instance type. This is done to preserve the instance type definition for long running instances (which may not -terminate for months or years). +terminate for months or years). If you are sure that you want to delete this instance +type from the database, pass the "--purge" flag after the name:: + + # nova-manage instance_type delete m1.xxlarge --purge + m1.xxlarge deleted -- cgit From ff0ef603fd3f87ad9294260d13ea3c122bab387f Mon Sep 17 00:00:00 2001 From: Ken Pepple Date: Thu, 17 Feb 2011 16:22:04 -0800 Subject: changed migration to 006 for trunk compatibility --- .../versions/004_add_instance_types.py | 86 ---------------------- .../versions/006_add_instance_types.py | 86 ++++++++++++++++++++++ 2 files changed, 86 insertions(+), 86 deletions(-) delete mode 100644 nova/db/sqlalchemy/migrate_repo/versions/004_add_instance_types.py create mode 100644 nova/db/sqlalchemy/migrate_repo/versions/006_add_instance_types.py diff --git a/nova/db/sqlalchemy/migrate_repo/versions/004_add_instance_types.py b/nova/db/sqlalchemy/migrate_repo/versions/004_add_instance_types.py deleted file mode 100644 index fec191214..000000000 --- a/nova/db/sqlalchemy/migrate_repo/versions/004_add_instance_types.py +++ /dev/null @@ -1,86 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# 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. - -from sqlalchemy import * -from migrate import * - -from nova import api -from nova import db -from nova import log as logging - -import datetime - -meta = MetaData() - - -# -# New Tables -# -instance_types = Table('instance_types', meta, - Column('created_at', DateTime(timezone=False)), - Column('updated_at', DateTime(timezone=False)), - Column('deleted_at', DateTime(timezone=False)), - Column('deleted', Boolean(create_constraint=True, name=None)), - Column('name', - String(length=255, convert_unicode=False, assert_unicode=None, - unicode_error=None, _warn_on_bytestring=False), - unique=True), - Column('id', Integer(), primary_key=True, nullable=False), - Column('memory_mb', Integer(), nullable=False), - Column('vcpus', Integer(), nullable=False), - Column('local_gb', Integer(), nullable=False), - Column('flavorid', Integer(), nullable=False, unique=True), - Column('swap', Integer(), nullable=False, default=0), - Column('rxtx_quota', Integer(), nullable=False, default=0), - Column('rxtx_cap', Integer(), nullable=False, default=0)) - - -def upgrade(migrate_engine): - # Upgrade operations go here - # Don't create your own engine; bind migrate_engine - # to your metadata - meta.bind = migrate_engine - try: - instance_types.create() - except Exception: - logging.info(repr(table)) - logging.exception('Exception while creating instance_types table') - raise - - # Here are the old static instance types - INSTANCE_TYPES = { - 'm1.tiny': dict(memory_mb=512, vcpus=1, local_gb=0, flavorid=1), - 'm1.small': dict(memory_mb=2048, vcpus=1, local_gb=20, flavorid=2), - 'm1.medium': dict(memory_mb=4096, vcpus=2, local_gb=40, flavorid=3), - 'm1.large': dict(memory_mb=8192, vcpus=4, local_gb=80, flavorid=4), - 'm1.xlarge': dict(memory_mb=16384, vcpus=8, local_gb=160, flavorid=5)} - try: - i = instance_types.insert() - for name, values in INSTANCE_TYPES.iteritems(): - # FIXME(kpepple) should we be seeding created_at / updated_at ? - # now = datetime.datatime.utcnow() - i.execute({'name': name, 'memory_mb': values["memory_mb"], - 'vcpus': values["vcpus"], 'deleted': 0, - 'local_gb': values["local_gb"], - 'flavorid': values["flavorid"]}) - except Exception: - logging.info(repr(table)) - logging.exception('Exception while seeding instance_types table') - raise - - -def downgrade(migrate_engine): - # Operations to reverse the above upgrade go here. - for table in (instance_types): - table.drop() diff --git a/nova/db/sqlalchemy/migrate_repo/versions/006_add_instance_types.py b/nova/db/sqlalchemy/migrate_repo/versions/006_add_instance_types.py new file mode 100644 index 000000000..fec191214 --- /dev/null +++ b/nova/db/sqlalchemy/migrate_repo/versions/006_add_instance_types.py @@ -0,0 +1,86 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# 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. + +from sqlalchemy import * +from migrate import * + +from nova import api +from nova import db +from nova import log as logging + +import datetime + +meta = MetaData() + + +# +# New Tables +# +instance_types = Table('instance_types', meta, + Column('created_at', DateTime(timezone=False)), + Column('updated_at', DateTime(timezone=False)), + Column('deleted_at', DateTime(timezone=False)), + Column('deleted', Boolean(create_constraint=True, name=None)), + Column('name', + String(length=255, convert_unicode=False, assert_unicode=None, + unicode_error=None, _warn_on_bytestring=False), + unique=True), + Column('id', Integer(), primary_key=True, nullable=False), + Column('memory_mb', Integer(), nullable=False), + Column('vcpus', Integer(), nullable=False), + Column('local_gb', Integer(), nullable=False), + Column('flavorid', Integer(), nullable=False, unique=True), + Column('swap', Integer(), nullable=False, default=0), + Column('rxtx_quota', Integer(), nullable=False, default=0), + Column('rxtx_cap', Integer(), nullable=False, default=0)) + + +def upgrade(migrate_engine): + # Upgrade operations go here + # Don't create your own engine; bind migrate_engine + # to your metadata + meta.bind = migrate_engine + try: + instance_types.create() + except Exception: + logging.info(repr(table)) + logging.exception('Exception while creating instance_types table') + raise + + # Here are the old static instance types + INSTANCE_TYPES = { + 'm1.tiny': dict(memory_mb=512, vcpus=1, local_gb=0, flavorid=1), + 'm1.small': dict(memory_mb=2048, vcpus=1, local_gb=20, flavorid=2), + 'm1.medium': dict(memory_mb=4096, vcpus=2, local_gb=40, flavorid=3), + 'm1.large': dict(memory_mb=8192, vcpus=4, local_gb=80, flavorid=4), + 'm1.xlarge': dict(memory_mb=16384, vcpus=8, local_gb=160, flavorid=5)} + try: + i = instance_types.insert() + for name, values in INSTANCE_TYPES.iteritems(): + # FIXME(kpepple) should we be seeding created_at / updated_at ? + # now = datetime.datatime.utcnow() + i.execute({'name': name, 'memory_mb': values["memory_mb"], + 'vcpus': values["vcpus"], 'deleted': 0, + 'local_gb': values["local_gb"], + 'flavorid': values["flavorid"]}) + except Exception: + logging.info(repr(table)) + logging.exception('Exception while seeding instance_types table') + raise + + +def downgrade(migrate_engine): + # Operations to reverse the above upgrade go here. + for table in (instance_types): + table.drop() -- cgit From 2da6494d20624177c0077d0709e1fdb0e5f8f03c Mon Sep 17 00:00:00 2001 From: Ken Pepple Date: Thu, 17 Feb 2011 17:42:49 -0800 Subject: pep8 --- nova/tests/api/openstack/test_zones.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/nova/tests/api/openstack/test_zones.py b/nova/tests/api/openstack/test_zones.py index 5542a1cf3..df497ef1b 100644 --- a/nova/tests/api/openstack/test_zones.py +++ b/nova/tests/api/openstack/test_zones.py @@ -57,8 +57,7 @@ def zone_get_all(context): dict(id=1, api_url='http://foo.com', username='bob', password='xxx'), dict(id=2, api_url='http://blah.com', username='alice', - password='qwerty') - ] + password='qwerty')] class ZonesTest(unittest.TestCase): -- cgit From 4b51ec3e9bca7421c66816c77c43396e51e68ea6 Mon Sep 17 00:00:00 2001 From: Cerberus Date: Thu, 17 Feb 2011 23:09:06 -0600 Subject: Tests --- nova/tests/api/openstack/common.py | 8 +++++--- nova/tests/test_compute.py | 7 +++++++ nova/virt/fake.py | 13 +++++++++++++ nova/virt/xenapi_conn.py | 8 -------- 4 files changed, 25 insertions(+), 11 deletions(-) diff --git a/nova/tests/api/openstack/common.py b/nova/tests/api/openstack/common.py index b55d3087b..66207cddc 100644 --- a/nova/tests/api/openstack/common.py +++ b/nova/tests/api/openstack/common.py @@ -21,10 +21,12 @@ import webob def webob_factory(url): base_url = url - def web_request(url, method, body=None): + def web_request(url, method=None, body=None): req = webob.Request.blank("%s%s" % (base_url, url)) - req.method = method - req.body = json.dumps(body) + if method: + req.method = method + if body: + req.body = json.dumps(body) return req return web_request diff --git a/nova/tests/test_compute.py b/nova/tests/test_compute.py index 2aa0690e7..e27e08827 100644 --- a/nova/tests/test_compute.py +++ b/nova/tests/test_compute.py @@ -258,3 +258,10 @@ class ComputeTestCase(test.TestCase): self.assertEqual(ret_val, None) self.compute.terminate_instance(self.context, instance_id) + + def test_resize_instance(self): + """Ensure instance can be migrated/resized""" + instance_id = self._create_instance() + self.compute.run_instnce(self.context, instance_id) + self.compute.prep_resize(self.context, instance_id) + diff --git a/nova/virt/fake.py b/nova/virt/fake.py index ff5e22603..da86df6d4 100644 --- a/nova/virt/fake.py +++ b/nova/virt/fake.py @@ -176,6 +176,19 @@ class FakeConnection(object): """ pass + def migrate_disk_and_power_off(self, instance, dest): + """ + Transfers the disk of a running instance in multiple phases, turning + off the instance before the end. + """ + pass + + def attach_disk(self, instance, disk_info): + """ + Attaches the disk to an instance given the metadata disk_info + """ + pass + def pause(self, instance, callback): """ Pause the specified instance. diff --git a/nova/virt/xenapi_conn.py b/nova/virt/xenapi_conn.py index aafd836e2..be018b47f 100644 --- a/nova/virt/xenapi_conn.py +++ b/nova/virt/xenapi_conn.py @@ -184,14 +184,6 @@ class XenAPIConnection(object): """Unpause paused VM instance""" self._vmops.unpause(instance, callback) - def power_off(self, instance): - """Shuts down a running VM instance""" - self._vmops._shutdown(instance, method='clean') - - def power_on(self, instance): - """powers on a powered off VM instance""" - self._vmops.power_on(instance) - def migrate_disk_and_power_off(self, instance, dest): """Transfers the VHD of a running instance to another host, then shuts off the instance copies over the COW disk""" -- cgit From 671766cb4ada59b0e575b395b5afff82950ddb76 Mon Sep 17 00:00:00 2001 From: "matt.dietz@rackspace.com" <> Date: Fri, 18 Feb 2011 06:03:15 +0000 Subject: Resize compute tests --- nova/tests/test_compute.py | 24 +++++++++++++++++++++--- nova/virt/fake.py | 6 ++++++ 2 files changed, 27 insertions(+), 3 deletions(-) diff --git a/nova/tests/test_compute.py b/nova/tests/test_compute.py index e27e08827..3f2e64c87 100644 --- a/nova/tests/test_compute.py +++ b/nova/tests/test_compute.py @@ -56,7 +56,7 @@ class ComputeTestCase(test.TestCase): self.manager.delete_project(self.project) super(ComputeTestCase, self).tearDown() - def _create_instance(self): + def _create_instance(self, params={}): """Create a test instance""" inst = {} inst['image_id'] = 'ami-test' @@ -67,6 +67,7 @@ class ComputeTestCase(test.TestCase): inst['instance_type'] = 'm1.tiny' inst['mac_address'] = utils.generate_mac() inst['ami_launch_index'] = 0 + inst.update(params) return db.instance_create(self.context, inst)['id'] def _create_group(self): @@ -262,6 +263,23 @@ class ComputeTestCase(test.TestCase): def test_resize_instance(self): """Ensure instance can be migrated/resized""" instance_id = self._create_instance() - self.compute.run_instnce(self.context, instance_id) - self.compute.prep_resize(self.context, instance_id) + context = self.context.elevated() + self.compute.run_instance(self.context, instance_id) + db.instance_update(self.context, instance_id, {'host':'foo'}) + + self.compute.prep_resize(context, instance_id) + migration_ref = db.migration_get_by_instance_and_status(context, + instance_id, 'pre-migrating') + self.compute.resize_instance(context, instance_id, + migration_ref['id']) + self.compute.terminate_instance(context, instance_id) + + def test_resize_same_source_fails(self): + """Ensure instance fails to migrate when source and destination are + the same host""" + instance_id = self._create_instance() + self.compute.run_instance(self.context, instance_id) + self.assertRaises(exception.Error, self.compute.prep_resize, + self.context, instance_id) + self.compute.terminate_instance(self.context, instance_id) diff --git a/nova/virt/fake.py b/nova/virt/fake.py index da86df6d4..9106ebf03 100644 --- a/nova/virt/fake.py +++ b/nova/virt/fake.py @@ -139,6 +139,12 @@ class FakeConnection(object): """ pass + def get_host_ip_addr(self): + """ + Retrieves the IP address of the dom0 + """ + pass + def resize(self, instance, flavor): """ Resizes/Migrates the specified instance. -- cgit From bb5624258200f027320327a38c524c389979c97a Mon Sep 17 00:00:00 2001 From: "matt.dietz@rackspace.com" <> Date: Fri, 18 Feb 2011 19:04:57 +0000 Subject: Resize compute tests --- nova/tests/test_xenapi.py | 34 ++++++++++++++++++++++++++++++++++ nova/tests/xenapi/stubs.py | 25 +++++++++++++++++++++++-- nova/virt/xenapi/fake.py | 2 +- 3 files changed, 58 insertions(+), 3 deletions(-) diff --git a/nova/tests/test_xenapi.py b/nova/tests/test_xenapi.py index 6b8efc9d8..ee4c68e6c 100644 --- a/nova/tests/test_xenapi.py +++ b/nova/tests/test_xenapi.py @@ -336,3 +336,37 @@ class XenAPIDiffieHellmanTestCase(test.TestCase): def tearDown(self): super(XenAPIDiffieHellmanTestCase, self).tearDown() + +class XenAPIMigrateInstance(test.TestCase): + """ + Unit test for verifying migration-related actions + """ + def setUp(self): + super(XenAPIMigrateInstance, self).setUp() + self.stubs = stubout.StubOutForTesting() + FLAGS.target_host = '127.0.0.1' + FLAGS.xenapi_connection_url = 'test_url' + FLAGS.xenapi_connection_password = 'test_pass' + db_fakes.stub_out_db_instance_api(self.stubs) + stubs.stub_out_get_target(self.stubs) + xenapi_fake.reset() + self.values = {'name': 1, 'id': 1, + 'project_id': 'fake', + 'user_id': 'fake', + 'image_id': 1, + 'kernel_id': 2, + 'ramdisk_id': 3, + 'instance_type': 'm1.large', + 'mac_address': 'aa:bb:cc:dd:ee:ff', + } + stubs.stub_out_migration_methods(self.stubs) + + def test_migrate_disk_and_power_off(self): + FLAGS.target_host = '127.0.0.1' + FLAGS.xenapi_connection_url = 'test_url' + FLAGS.xenapi_connection_password = 'test_pass' + destination = '127.0.0.1' + instance = db.instance_create(self.values) + stubs.stubout_session(self.stubs, stubs.FakeSessionForMigrationTests) + conn = xenapi_conn.get_connection(False) + conn.migrate_disk_and_power_off(instance, destination) diff --git a/nova/tests/xenapi/stubs.py b/nova/tests/xenapi/stubs.py index 624995ada..d1c367475 100644 --- a/nova/tests/xenapi/stubs.py +++ b/nova/tests/xenapi/stubs.py @@ -20,6 +20,7 @@ from nova.virt import xenapi_conn from nova.virt.xenapi import fake from nova.virt.xenapi import volume_utils from nova.virt.xenapi import vm_utils +from nova.virt.xenapi import vmops def stubout_instance_snapshot(stubs): @@ -170,8 +171,8 @@ class FakeSessionForVMTests(fake.SessionBase): def VM_destroy(self, session_ref, vm_ref): fake.destroy_vm(vm_ref) - - + + class FakeSessionForVolumeTests(fake.SessionBase): """ Stubs out a XenAPISession for Volume tests """ def __init__(self, uri): @@ -205,3 +206,23 @@ class FakeSessionForVolumeFailedTests(FakeSessionForVolumeTests): def SR_forget(self, _1, ref): pass + +class FakeSessionForMigrationTests(fake.SessionBase): + """ Stubs out a XenAPISession for Migration tests """ + def __init__(self, uri): + super(FakeSessionForMigrationTests, self).__init__(uri) + + +class FakeSnapshot(vmops.VMOps): + def __getattr__(self, key): + return 'fake' + + def __exit__(self, type, value, traceback) + pass + +def fake_get_snapshot(self, instance): + return FakeSnapshot() + +def stub_out_migration_methods(stubs): + stubs.Set(vmops.VMOps, '_get_snapshot', + fake_get_snapshot) diff --git a/nova/virt/xenapi/fake.py b/nova/virt/xenapi/fake.py index 018d0dcd3..e1ae03e70 100644 --- a/nova/virt/xenapi/fake.py +++ b/nova/virt/xenapi/fake.py @@ -401,7 +401,7 @@ class SessionBase(object): field in _db_content[cls][ref]): return _db_content[cls][ref][field] - LOG.debuug(_('Raising NotImplemented')) + LOG.debug(_('Raising NotImplemented')) raise NotImplementedError( _('xenapi.fake does not have an implementation for %s or it has ' 'been called with the wrong number of arguments') % name) -- cgit From 62b3eb71384581e900b061e65caa6418c4452fa9 Mon Sep 17 00:00:00 2001 From: "matt.dietz@rackspace.com" <> Date: Fri, 18 Feb 2011 21:37:57 +0000 Subject: XenAPI tests --- nova/tests/test_xenapi.py | 12 +++++++----- nova/tests/xenapi/stubs.py | 38 +++++++++++++++++++++++++++++--------- nova/virt/xenapi/fake.py | 3 +++ 3 files changed, 39 insertions(+), 14 deletions(-) diff --git a/nova/tests/test_xenapi.py b/nova/tests/test_xenapi.py index ee4c68e6c..3cbc01e5c 100644 --- a/nova/tests/test_xenapi.py +++ b/nova/tests/test_xenapi.py @@ -362,11 +362,13 @@ class XenAPIMigrateInstance(test.TestCase): stubs.stub_out_migration_methods(self.stubs) def test_migrate_disk_and_power_off(self): - FLAGS.target_host = '127.0.0.1' - FLAGS.xenapi_connection_url = 'test_url' - FLAGS.xenapi_connection_password = 'test_pass' - destination = '127.0.0.1' instance = db.instance_create(self.values) stubs.stubout_session(self.stubs, stubs.FakeSessionForMigrationTests) conn = xenapi_conn.get_connection(False) - conn.migrate_disk_and_power_off(instance, destination) + conn.migrate_disk_and_power_off(instance, '127.0.0.1') + + def test_attach_disk(self): + instance = db.instance_create(self.values) + stubs.stubout_session(self.stubs, stubs.FakeSessionForMigrationTests) + conn = xenapi_conn.get_connection(False) + conn.attach_disk(instance, {'base_copy': 'hurr', 'cow': 'durr'}) diff --git a/nova/tests/xenapi/stubs.py b/nova/tests/xenapi/stubs.py index d1c367475..054fc434b 100644 --- a/nova/tests/xenapi/stubs.py +++ b/nova/tests/xenapi/stubs.py @@ -213,16 +213,36 @@ class FakeSessionForMigrationTests(fake.SessionBase): super(FakeSessionForMigrationTests, self).__init__(uri) -class FakeSnapshot(vmops.VMOps): - def __getattr__(self, key): - return 'fake' +def stub_out_migration_methods(stubs): + class FakeSnapshot(object): + def __getattr__(self, key): + return str(key) + + def __enter__(self): + return self + + def __exit__(self, type, value, traceback): + pass + + def fake_get_snapshot(self, instance): + return FakeSnapshot() - def __exit__(self, type, value, traceback) + @classmethod + def fake_get_vdi(cls, session, vm_ref): + vdi_ref = fake.create_vdi(name_label='derp', read_only=False, + sr_ref='herp', sharable=False) + vdi_rec = session.get_xenapi().VDI.get_record(vdi_ref) + return vdi_ref, {'uuid': vdi_rec['uuid']} + + def fake_shutdown(self, inst, vm, method='clean'): pass -def fake_get_snapshot(self, instance): - return FakeSnapshot() + @classmethod + def fake_scan_sr(cls, session): + pass -def stub_out_migration_methods(stubs): - stubs.Set(vmops.VMOps, '_get_snapshot', - fake_get_snapshot) + stubs.Set(vm_utils.VMHelper, 'scan_sr', fake_scan_sr) + stubs.Set(vmops.VMOps, '_get_snapshot', fake_get_snapshot) + stubs.Set(vm_utils.VMHelper, 'get_vdi_for_vm_safely', fake_get_vdi) + stubs.Set(xenapi_conn.XenAPISession, 'wait_for_task', lambda x,y,z: None) + stubs.Set(vmops.VMOps, '_shutdown', fake_shutdown) diff --git a/nova/virt/xenapi/fake.py b/nova/virt/xenapi/fake.py index e1ae03e70..ba12d4d3a 100644 --- a/nova/virt/xenapi/fake.py +++ b/nova/virt/xenapi/fake.py @@ -290,6 +290,9 @@ class SessionBase(object): #Always return 12GB available return 12 * 1024 * 1024 * 1024 + def host_call_plugin(*args): + return 'herp' + def xenapi_request(self, methodname, params): if methodname.startswith('login'): self._login(methodname, params) -- cgit From a43c5929de7ebf58eb9ecb8416ce3cf4194c176a Mon Sep 17 00:00:00 2001 From: Cerberus Date: Fri, 18 Feb 2011 16:13:34 -0600 Subject: Pep8 cleanup --- nova/api/openstack/servers.py | 13 ++-- nova/compute/api.py | 12 ++- nova/compute/manager.py | 85 +++++++++++----------- nova/db/api.py | 5 +- nova/db/sqlalchemy/api.py | 6 +- .../versions/004_add_instance_migrations.py | 3 +- nova/db/sqlalchemy/models.py | 5 +- nova/tests/api/openstack/common.py | 7 +- nova/tests/api/openstack/test_servers.py | 24 +++--- nova/tests/test_compute.py | 6 +- nova/tests/test_xenapi.py | 1 + nova/tests/xenapi/stubs.py | 11 +-- nova/virt/xenapi/vm_utils.py | 13 ++-- nova/virt/xenapi/vmops.py | 5 +- nova/virt/xenapi_conn.py | 6 +- 15 files changed, 104 insertions(+), 98 deletions(-) diff --git a/nova/api/openstack/servers.py b/nova/api/openstack/servers.py index a719f5e15..f68c97323 100644 --- a/nova/api/openstack/servers.py +++ b/nova/api/openstack/servers.py @@ -209,12 +209,12 @@ class Controller(wsgi.Controller): resize a server """ actions = { - 'reboot':self._action_reboot, - 'resize':self._action_resize, - 'confirmResize':self._action_confirm_resize, - 'revertResize':self._action_revert_resize, - 'rebuild':self._action_rebuild - } + 'reboot': self._action_reboot, + 'resize': self._action_resize, + 'confirmResize': self._action_confirm_resize, + 'revertResize': self._action_revert_resize, + 'rebuild': self._action_rebuild, + } input_dict = self._deserialize(req.body, req) for key in actions.keys(): @@ -256,7 +256,6 @@ class Controller(wsgi.Controller): return faults.Fault(exc.HTTPBadRequest()) return faults.Fault(exc.HTTPAccepted()) - def _action_reboot(self, input_dict, req, id): try: reboot_type = input_dict['reboot']['type'] diff --git a/nova/compute/api.py b/nova/compute/api.py index 371cbae5f..2eb0e0743 100644 --- a/nova/compute/api.py +++ b/nova/compute/api.py @@ -408,7 +408,7 @@ class API(base.Base): raise exception.Error(_("No finished migrations found for \ instance")) - params = { 'migration_id': migration_ref['id'] } + params = {'migration_id': migration_ref['id']} self._cast_compute_message('revert_resize', context, instance_id, migration_ref['dest_compute'], params=params) @@ -422,14 +422,12 @@ class API(base.Base): raise exception.Error(_("No finished migrations found for \ instance")) instance_ref = self.db.instance_get(context, instance_id) - - params = { 'migration_id': migration_ref['id'] } + params = {'migration_id': migration_ref['id']} self._cast_compute_message('confirm_resize', context, instance_id, migration_ref['source_compute'], params=params) - self.db.migration_update(context, migration_id, - { 'status': 'confirmed' }) - + self.db.migration_update(context, migration_id, + {'status': 'confirmed'}) self.db.instance_update(context, instance_id, {'host': migration_ref['dest_compute'], }) @@ -439,7 +437,7 @@ class API(base.Base): {"method": "prep_resize", "args": {"topic": FLAGS.compute_topic, "instance_id": instance_id, }},) - + def pause(self, context, instance_id): """Pause the given instance.""" self._cast_compute_message('pause_instance', context, instance_id) diff --git a/nova/compute/manager.py b/nova/compute/manager.py index 1e1c44663..3f6c359ba 100644 --- a/nova/compute/manager.py +++ b/nova/compute/manager.py @@ -379,7 +379,7 @@ class ComputeManager(manager.Manager): def _update_state_callback(self, context, instance_id, result): """Update instance state when async task completes.""" self._update_state(context, instance_id) - + @exception.wrap_exception @checks_instance_lock def confirm_resize(self, context, instance_id, migration_id): @@ -392,8 +392,8 @@ class ComputeManager(manager.Manager): @exception.wrap_exception @checks_instance_lock def revert_resize(self, context, instance_id, migration_id): - """Destroys the new instance on the destination machine, - reverts the model changes, and powers on the old + """Destroys the new instance on the destination machine, + reverts the model changes, and powers on the old instance on the source machine""" instance_ref = self.db.instance_get(context, instance_id) migration_ref = self.db.migration_get(context, migration_id) @@ -401,24 +401,23 @@ class ComputeManager(manager.Manager): #TODO(mdietz): we may want to split these into separate methods. if migration_ref['source_compute'] == FLAGS.host: self.driver.power_on(instance_ref) - self.db.migration_update(context, migration_id, - { 'status': 'reverted' }) + self.db.migration_update(context, migration_id, + {'status': 'reverted'}) else: self.driver.destroy(instance_ref) - topic = self.db.queue_get_for(context, FLAGS.compute_topic, + topic = self.db.queue_get_for(context, FLAGS.compute_topic, instance_ref['host']) - rpc.cast(context, topic, - { 'method': 'revert_resize', - 'args': { - 'migration_id': migration_ref['id'], - 'instance_id': instance_id, - }, + rpc.cast(context, topic, + {'method': 'revert_resize', + 'args': { + 'migration_id': migration_ref['id'], + 'instance_id': instance_id, }, }) @exception.wrap_exception @checks_instance_lock def prep_resize(self, context, instance_id): - """Initiates the process of moving a running instance to another + """Initiates the process of moving a running instance to another host, possibly changing the RAM and disk size in the process""" context = context.elevated() instance_ref = self.db.instance_get(context, instance_id) @@ -426,21 +425,21 @@ class ComputeManager(manager.Manager): raise exception.Error(_( 'Migration error: destination same as source!')) - migration_ref = self.db.migration_create(context, - { 'instance_id': instance_id, - 'source_compute': instance_ref['host'], - 'dest_compute': FLAGS.host, - 'dest_host': self.driver.get_host_ip_addr(), - 'status': 'pre-migrating' }) - LOG.audit(_('instance %s: migrating to '), instance_id, context=context) - topic = self.db.queue_get_for(context, FLAGS.compute_topic, + migration_ref = self.db.migration_create(context, + {'instance_id': instance_id, + 'source_compute': instance_ref['host'], + 'dest_compute': FLAGS.host, + 'dest_host': self.driver.get_host_ip_addr(), + 'status': 'pre-migrating'}) + LOG.audit(_('instance %s: migrating to '), instance_id, + context=context) + topic = self.db.queue_get_for(context, FLAGS.compute_topic, instance_ref['host']) - rpc.cast(context, topic, - { 'method': 'resize_instance', - 'args': { - 'migration_id': migration_ref['id'], - 'instance_id': instance_id, - }, + rpc.cast(context, topic, + {'method': 'resize_instance', + 'args': { + 'migration_id': migration_ref['id'], + 'instance_id': instance_id, }, }) @exception.wrap_exception @@ -449,28 +448,26 @@ class ComputeManager(manager.Manager): """Starts the migration of a running instance to another host""" migration_ref = self.db.migration_get(context, migration_id) instance_ref = self.db.instance_get(context, instance_id) - self.db.migration_update(context, migration_id, - { 'status': 'migrating', }) + self.db.migration_update(context, migration_id, + {'status': 'migrating', }) - disk_info = self.driver.migrate_disk_and_power_off(instance_ref, + disk_info = self.driver.migrate_disk_and_power_off(instance_ref, migration_ref['dest_host']) - - self.db.migration_update(context, migration_id, - { 'status': 'post-migrating', }) + self.db.migration_update(context, migration_id, + {'status': 'post-migrating', }) - #TODO(mdietz): This is where we would update the VM record + #TODO(mdietz): This is where we would update the VM record #after resizing service = self.db.service_get_by_host_and_topic(context, migration_ref['dest_compute'], FLAGS.compute_topic) - topic = self.db.queue_get_for(context, FLAGS.compute_topic, + topic = self.db.queue_get_for(context, FLAGS.compute_topic, migration_ref['dest_compute']) - rpc.cast(context, topic, - { 'method': 'finish_resize', - 'args': { - 'migration_id': migration_id, - 'instance_id': instance_id, - 'disk_info': disk_info, - }, + rpc.cast(context, topic, + {'method': 'finish_resize', + 'args': { + 'migration_id': migration_id, + 'instance_id': instance_id, + 'disk_info': disk_info, }, }) @exception.wrap_exception @@ -486,8 +483,8 @@ class ComputeManager(manager.Manager): new_disk_info = self.driver.attach_disk(instance_ref, disk_info) self.driver.spawn(instance_ref, disk=new_disk_info) - self.db.migration_update(context, migration_id, - {'status': 'finished', }) + self.db.migration_update(context, migration_id, + {'status': 'finished', }) @exception.wrap_exception @checks_instance_lock diff --git a/nova/db/api.py b/nova/db/api.py index 5a9d49374..8706ef3d6 100644 --- a/nova/db/api.py +++ b/nova/db/api.py @@ -260,17 +260,20 @@ def floating_ip_get_by_address(context, address): #################### def migration_update(context, id, values): - """Update a migration instance""" + """Update a migration instance""" return IMPL.migration_update(context, id, values) + def migration_create(context, values): """Create a migration record""" return IMPL.migration_create(context, values) + def migration_get(context, migration_id): """Finds a migration by the id""" return IMPL.migration_get(context, migration_id) + def migration_get_by_instance_and_status(context, instance_id, status): """Finds a migration by the instance id its migrating""" return IMPL.migration_get_by_instance_and_status(context, instance_id, diff --git a/nova/db/sqlalchemy/api.py b/nova/db/sqlalchemy/api.py index 210b53296..facb46b8b 100644 --- a/nova/db/sqlalchemy/api.py +++ b/nova/db/sqlalchemy/api.py @@ -156,6 +156,7 @@ def service_get_all_by_topic(context, topic): filter_by(topic=topic).\ all() + @require_admin_context def service_get_by_host_and_topic(context, host, topic): session = get_session() @@ -166,6 +167,7 @@ def service_get_by_host_and_topic(context, host, topic): filter_by(topic=topic).\ first() + @require_admin_context def service_get_all_by_host(context, host): session = get_session() @@ -1996,7 +1998,7 @@ def migration_get(context, id, session=None): result = session.query(models.Migration).\ filter_by(id=id).first() if not result: - raise exception.NotFound(_("No migration found with id %s") + raise exception.NotFound(_("No migration found with id %s") % migration_id) return result @@ -2008,7 +2010,7 @@ def migration_get_by_instance_and_status(context, instance_id, status): filter_by(instance_id=instance_id).\ filter_by(status=status).first() if not result: - raise exception.NotFound(_("No migration found with instance id %s") + raise exception.NotFound(_("No migration found with instance id %s") % migration_id) return result diff --git a/nova/db/sqlalchemy/migrate_repo/versions/004_add_instance_migrations.py b/nova/db/sqlalchemy/migrate_repo/versions/004_add_instance_migrations.py index 4aab5bdc6..4fda525f1 100644 --- a/nova/db/sqlalchemy/migrate_repo/versions/004_add_instance_migrations.py +++ b/nova/db/sqlalchemy/migrate_repo/versions/004_add_instance_migrations.py @@ -44,9 +44,10 @@ migrations = Table('migrations', meta, Column('dest_host', String(255)), Column('instance_id', Integer, ForeignKey('instances.id'), nullable=True), - Column('status', String(255)) + Column('status', String(255)), ) + def upgrade(migrate_engine): # Upgrade operations go here. Don't create your own engine; # bind migrate_engine to your metadata diff --git a/nova/db/sqlalchemy/models.py b/nova/db/sqlalchemy/models.py index 0140fbeab..b05f134b7 100644 --- a/nova/db/sqlalchemy/models.py +++ b/nova/db/sqlalchemy/models.py @@ -374,7 +374,8 @@ class Migration(BASE, NovaBase): dest_compute = Column(String(255)) dest_host = Column(String(255)) instance_id = Column(Integer, ForeignKey('instances.id'), nullable=True) - status = Column(String(255)) #TODO(_cerberus_): enum + #TODO(_cerberus_): enum + status = Column(String(255)) class Network(BASE, NovaBase): @@ -559,7 +560,7 @@ def register_models(): Volume, ExportDevice, IscsiTarget, FixedIp, FloatingIp, Network, SecurityGroup, SecurityGroupIngressRule, SecurityGroupInstanceAssociation, AuthToken, User, - Project, Certificate, ConsolePool, Console, + Project, Certificate, ConsolePool, Console, Migration) # , Image, Host engine = create_engine(FLAGS.sql_connection, echo=False) for model in models: diff --git a/nova/tests/api/openstack/common.py b/nova/tests/api/openstack/common.py index 66207cddc..3f9c7d3cf 100644 --- a/nova/tests/api/openstack/common.py +++ b/nova/tests/api/openstack/common.py @@ -19,14 +19,17 @@ import json import webob + def webob_factory(url): + """Factory for removing duplicate webob code from tests""" + base_url = url + def web_request(url, method=None, body=None): - req = webob.Request.blank("%s%s" % (base_url, url)) + req = webob.Request.blank("%s%s" % (base_url, url)) if method: req.method = method if body: req.body = json.dumps(body) return req return web_request - diff --git a/nova/tests/api/openstack/test_servers.py b/nova/tests/api/openstack/test_servers.py index 665551c55..4eb4a3c62 100644 --- a/nova/tests/api/openstack/test_servers.py +++ b/nova/tests/api/openstack/test_servers.py @@ -419,8 +419,9 @@ class ServersTest(unittest.TestCase): req = self.webreq('/1/action', 'POST', dict(resize=dict(flavorId=3))) self.resize_called = False + def resize_mock(*args): - self.resize_called = True + self.resize_called = True self.stubs.Set(nova.compute.api.API, 'resize', resize_mock) @@ -432,8 +433,9 @@ class ServersTest(unittest.TestCase): req = self.webreq('/1/action', 'POST', dict(resize=dict(derp=3))) self.resize_called = False + def resize_mock(*args): - self.resize_called = True + self.resize_called = True self.stubs.Set(nova.compute.api.API, 'resize', resize_mock) @@ -445,7 +447,7 @@ class ServersTest(unittest.TestCase): req = self.webreq('/1/action', 'POST', dict(resize=dict(flavorId=3))) def resize_mock(*args): - raise Exception, 'hurr durr' + raise Exception('hurr durr') self.stubs.Set(nova.compute.api.API, 'resize', resize_mock) @@ -456,10 +458,11 @@ class ServersTest(unittest.TestCase): req = self.webreq('/1/action', 'POST', dict(confirmResize=None)) self.resize_called = False + def confirm_resize_mock(*args): - self.resize_called = True + self.resize_called = True - self.stubs.Set(nova.compute.api.API, 'confirm_resize', + self.stubs.Set(nova.compute.api.API, 'confirm_resize', confirm_resize_mock) res = req.get_response(fakes.wsgi_app()) @@ -470,9 +473,9 @@ class ServersTest(unittest.TestCase): req = self.webreq('/1/action', 'POST', dict(confirmResize=None)) def confirm_resize_mock(*args): - raise Exception, 'hurr durr' + raise Exception('hurr durr') - self.stubs.Set(nova.compute.api.API, 'confirm_resize', + self.stubs.Set(nova.compute.api.API, 'confirm_resize', confirm_resize_mock) res = req.get_response(fakes.wsgi_app()) @@ -482,10 +485,11 @@ class ServersTest(unittest.TestCase): req = self.webreq('/1/action', 'POST', dict(revertResize=None)) self.resize_called = False + def revert_resize_mock(*args): self.resize_called = True - self.stubs.Set(nova.compute.api.API, 'revert_resize', + self.stubs.Set(nova.compute.api.API, 'revert_resize', revert_resize_mock) res = req.get_response(fakes.wsgi_app()) @@ -496,9 +500,9 @@ class ServersTest(unittest.TestCase): req = self.webreq('/1/action', 'POST', dict(revertResize=None)) def revert_resize_mock(*args): - raise Exception, 'hurr durr' + raise Exception('hurr durr') - self.stubs.Set(nova.compute.api.API, 'revert_resize', + self.stubs.Set(nova.compute.api.API, 'revert_resize', revert_resize_mock) res = req.get_response(fakes.wsgi_app()) diff --git a/nova/tests/test_compute.py b/nova/tests/test_compute.py index 3f2e64c87..5fd1ddaec 100644 --- a/nova/tests/test_compute.py +++ b/nova/tests/test_compute.py @@ -265,8 +265,7 @@ class ComputeTestCase(test.TestCase): instance_id = self._create_instance() context = self.context.elevated() self.compute.run_instance(self.context, instance_id) - db.instance_update(self.context, instance_id, {'host':'foo'}) - + db.instance_update(self.context, instance_id, {'host': 'foo'}) self.compute.prep_resize(context, instance_id) migration_ref = db.migration_get_by_instance_and_status(context, instance_id, 'pre-migrating') @@ -279,7 +278,6 @@ class ComputeTestCase(test.TestCase): the same host""" instance_id = self._create_instance() self.compute.run_instance(self.context, instance_id) - self.assertRaises(exception.Error, self.compute.prep_resize, + self.assertRaises(exception.Error, self.compute.prep_resize, self.context, instance_id) - self.compute.terminate_instance(self.context, instance_id) diff --git a/nova/tests/test_xenapi.py b/nova/tests/test_xenapi.py index 3cbc01e5c..cb9b6620a 100644 --- a/nova/tests/test_xenapi.py +++ b/nova/tests/test_xenapi.py @@ -337,6 +337,7 @@ class XenAPIDiffieHellmanTestCase(test.TestCase): def tearDown(self): super(XenAPIDiffieHellmanTestCase, self).tearDown() + class XenAPIMigrateInstance(test.TestCase): """ Unit test for verifying migration-related actions diff --git a/nova/tests/xenapi/stubs.py b/nova/tests/xenapi/stubs.py index 054fc434b..303c37eb9 100644 --- a/nova/tests/xenapi/stubs.py +++ b/nova/tests/xenapi/stubs.py @@ -171,8 +171,8 @@ class FakeSessionForVMTests(fake.SessionBase): def VM_destroy(self, session_ref, vm_ref): fake.destroy_vm(vm_ref) - - + + class FakeSessionForVolumeTests(fake.SessionBase): """ Stubs out a XenAPISession for Volume tests """ def __init__(self, uri): @@ -207,6 +207,7 @@ class FakeSessionForVolumeFailedTests(FakeSessionForVolumeTests): def SR_forget(self, _1, ref): pass + class FakeSessionForMigrationTests(fake.SessionBase): """ Stubs out a XenAPISession for Migration tests """ def __init__(self, uri): @@ -232,8 +233,8 @@ def stub_out_migration_methods(stubs): vdi_ref = fake.create_vdi(name_label='derp', read_only=False, sr_ref='herp', sharable=False) vdi_rec = session.get_xenapi().VDI.get_record(vdi_ref) - return vdi_ref, {'uuid': vdi_rec['uuid']} - + return vdi_ref, {'uuid': vdi_rec['uuid'], } + def fake_shutdown(self, inst, vm, method='clean'): pass @@ -244,5 +245,5 @@ def stub_out_migration_methods(stubs): stubs.Set(vm_utils.VMHelper, 'scan_sr', fake_scan_sr) stubs.Set(vmops.VMOps, '_get_snapshot', fake_get_snapshot) stubs.Set(vm_utils.VMHelper, 'get_vdi_for_vm_safely', fake_get_vdi) - stubs.Set(xenapi_conn.XenAPISession, 'wait_for_task', lambda x,y,z: None) + stubs.Set(xenapi_conn.XenAPISession, 'wait_for_task', lambda x, y, z: None) stubs.Set(vmops.VMOps, '_shutdown', fake_shutdown) diff --git a/nova/virt/xenapi/vm_utils.py b/nova/virt/xenapi/vm_utils.py index 91e7339b1..436c88023 100644 --- a/nova/virt/xenapi/vm_utils.py +++ b/nova/virt/xenapi/vm_utils.py @@ -256,21 +256,18 @@ class VMHelper(HelperBase): else: num_vdis = len(vdi_refs) if num_vdis != 1: - raise Exception(_("Unexpected number of VDIs (%(num_vdis)s) found" + raise Exception( + _("Unexpected number of VDIs (%(num_vdis)s) found" " for VM %(vm_ref)s") % locals()) vdi_ref = vdi_refs[0] vdi_rec = session.get_xenapi().VDI.get_record(vdi_ref) return vdi_ref, vdi_rec - - - @classmethod def create_snapshot(cls, session, instance_id, vm_ref, label): """ Creates Snapshot (Template) VM, Snapshot VBD, Snapshot VDI, - Snapshot VHD - """ + Snapshot VHD """ #TODO(sirp): Add quiesce and VSS locking support when Windows support # is added LOG.debug(_("Snapshotting VM %(vm_ref)s with label '%(label)s'...") @@ -284,7 +281,7 @@ class VMHelper(HelperBase): task = session.call_xenapi('Async.VM.snapshot', vm_ref, label) template_vm_ref = session.wait_for_task(instance_id, task) - template_vdi_rec = cls.get_vdi_for_vm_safely(session, + template_vdi_rec = cls.get_vdi_for_vm_safely(session, template_vm_ref)[1] template_vdi_uuid = template_vdi_rec["uuid"] @@ -299,7 +296,7 @@ class VMHelper(HelperBase): @classmethod def get_sr(cls, session, sr_label='slices'): - """ Finds the SR named by the given name label and returns + """ Finds the SR named by the given name label and returns the UUID """ return session.call_xenapi('SR.get_by_name_label', sr_label)[0] diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py index 552c2ddd1..d457f2e3f 100644 --- a/nova/virt/xenapi/vmops.py +++ b/nova/virt/xenapi/vmops.py @@ -345,7 +345,7 @@ class VMOps(object): 'new_base_copy_uuid': new_base_copy_uuid, 'new_cow_uuid': new_cow_uuid, } - task = self._session.async_call_plugin('migration', + task = self._session.async_call_plugin('migration', 'move_vhds_into_sr', {'params': pickle.dumps(params)}) self._session.wait_for_task(instance.id, task) @@ -469,7 +469,8 @@ class VMOps(object): vm = VMHelper.lookup(self._session, instance.name) return self._destroy(instance, vm, shutdown=True) - def _destroy(self, instance, vm, shutdown=True, destroy_kernel_ramdisk=True): + def _destroy(self, instance, vm, shutdown=True, + destroy_kernel_ramdisk=True): """ Destroys VM instance by performing: diff --git a/nova/virt/xenapi_conn.py b/nova/virt/xenapi_conn.py index be018b47f..e1c5dcc7c 100644 --- a/nova/virt/xenapi_conn.py +++ b/nova/virt/xenapi_conn.py @@ -192,7 +192,7 @@ class XenAPIConnection(object): def attach_disk(self, instance, disk_info): """Moves the copied VDIs into the SR""" return self._vmops.attach_disk(instance, disk_info) - + def suspend(self, instance, callback): """suspend the specified instance""" self._vmops.suspend(instance, callback) @@ -220,9 +220,9 @@ class XenAPIConnection(object): def get_ajax_console(self, instance): """Return link to instance's ajax console""" return self._vmops.get_ajax_console(instance) - + def get_host_ip_addr(self): - xs_url = urlparse.urlparse(FLAGS.xenapi_connection_url) + xs_url = urlparse.urlparse(FLAGS.xenapi_connection_url) return xs_url.netloc def attach_volume(self, instance_name, device_path, mountpoint): -- cgit From fcd31c4d7c3855cb95ac75d6966b377eca8bbe7d Mon Sep 17 00:00:00 2001 From: Ken Pepple Date: Fri, 18 Feb 2011 15:59:42 -0800 Subject: added instance types purge test --- nova/tests/test_instance_types.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/nova/tests/test_instance_types.py b/nova/tests/test_instance_types.py index 0d54cc283..fe052110f 100644 --- a/nova/tests/test_instance_types.py +++ b/nova/tests/test_instance_types.py @@ -49,12 +49,16 @@ class InstanceTypeTestCase(test.TestCase): new = instance_types.get_all_types() self.assertNotEqual(len(starting_inst_list), len(new), - 'instance was not created') + 'instance type was not created') instance_types.destroy(self.name) self.assertEqual(1, instance_types.get_instance_type(self.name)["deleted"]) self.assertEqual(starting_inst_list, instance_types.get_all_types()) db.instance_type_purge(context.get_admin_context(), self.name) + self.assertEqual(len(starting_inst_list), + len(instance_types.get_all_types()), + 'instance type not purged') + def test_get_all_instance_types(self): """Ensures that all instance types can be retrieved""" -- cgit From 3609f1da7e1383b76295c6e8bd1d1dc0d798aa63 Mon Sep 17 00:00:00 2001 From: Ken Pepple Date: Fri, 18 Feb 2011 16:00:22 -0800 Subject: pep8 --- nova/tests/test_instance_types.py | 1 - 1 file changed, 1 deletion(-) diff --git a/nova/tests/test_instance_types.py b/nova/tests/test_instance_types.py index fe052110f..705144cb0 100644 --- a/nova/tests/test_instance_types.py +++ b/nova/tests/test_instance_types.py @@ -59,7 +59,6 @@ class InstanceTypeTestCase(test.TestCase): len(instance_types.get_all_types()), 'instance type not purged') - def test_get_all_instance_types(self): """Ensures that all instance types can be retrieved""" session = get_session() -- cgit From 53784c1afaa12d0a8b22248093ec1e623a1a913d Mon Sep 17 00:00:00 2001 From: Ken Pepple Date: Fri, 18 Feb 2011 17:17:47 -0800 Subject: added purge option and tightened up testing --- bin/nova-manage | 15 ++++++---- nova/compute/instance_types.py | 13 ++++++++ nova/tests/test_instance_types.py | 2 +- nova/tests/test_nova_manage.py | 62 ++++++++++++++++++++------------------- 4 files changed, 56 insertions(+), 36 deletions(-) diff --git a/bin/nova-manage b/bin/nova-manage index 3318a593e..8ecf626a4 100755 --- a/bin/nova-manage +++ b/bin/nova-manage @@ -683,9 +683,9 @@ class InstanceTypeCommands(object): vcpus, local_gb, flavorid, - swap, - rxtx_quota, - rxtx_cap): + swap=0, + rxtx_quota=0, + rxtx_cap=0): """Creates instance types / flavors arguments: name memory vcpus local_gb flavorid swap rxtx_quota rxtx_cap @@ -719,13 +719,18 @@ class InstanceTypeCommands(object): try: if purge == "--purge": instance_types.purge(name) - verb = "deleted" + verb = "purged" else: instance_types.destroy(name) - verb = "purged" + verb = "deleted" except exception.ApiError: print "Valid instance type name is required" sys.exit(1) + except exception.DBError, e: + print "DB Error: %s" % e + sys.exit(2) + except: + sys.exit(3) else: print "%s %s" % (name, verb) diff --git a/nova/compute/instance_types.py b/nova/compute/instance_types.py index a5e0a7baf..ce4b25964 100644 --- a/nova/compute/instance_types.py +++ b/nova/compute/instance_types.py @@ -81,6 +81,19 @@ def destroy(name): name) +def purge(name): + """Removes instance types / flavors from database + arguments: name""" + if name == None: + raise exception.InvalidInputException(_("No instance type specified")) + else: + try: + db.instance_type_purge(context.get_admin_context(), name) + except exception.NotFound: + raise exception.ApiError(_("Unknown instance type: %s"), + name) + + def get_all_types(inactive=0): """Retrieves non-deleted instance_types. Pass true as argument if you want deleted instance types returned also.""" diff --git a/nova/tests/test_instance_types.py b/nova/tests/test_instance_types.py index 705144cb0..1b192ca28 100644 --- a/nova/tests/test_instance_types.py +++ b/nova/tests/test_instance_types.py @@ -54,7 +54,7 @@ class InstanceTypeTestCase(test.TestCase): self.assertEqual(1, instance_types.get_instance_type(self.name)["deleted"]) self.assertEqual(starting_inst_list, instance_types.get_all_types()) - db.instance_type_purge(context.get_admin_context(), self.name) + instance_types.purge(self.name) self.assertEqual(len(starting_inst_list), len(instance_types.get_all_types()), 'instance type not purged') diff --git a/nova/tests/test_nova_manage.py b/nova/tests/test_nova_manage.py index 03b4e3fb8..03aea53c9 100644 --- a/nova/tests/test_nova_manage.py +++ b/nova/tests/test_nova_manage.py @@ -33,12 +33,13 @@ class NovaManageTestCase(test.TestCase): order_by("flavorid desc").first() self.flavorid = str(max_flavorid["flavorid"] + 1) self.name = str(int(time.time())) + self.fnull = open(os.devnull, 'w') def teardown(self): - fnull.close() + self.fnull.close() def test_create_and_delete_instance_types(self): - fnull = open(os.devnull, 'w') + myname = self.name + "create_and_delete" retcode = subprocess.call([ "bin/nova-manage", "instance_type", @@ -51,45 +52,44 @@ class NovaManageTestCase(test.TestCase): "2", "10", "10"], - stdout=fnull) + stdout=self.fnull) self.assertEqual(0, retcode) - retcode = subprocess.call(["bin/nova-manage", "instance_type",\ - "delete", self.name], stdout=fnull) + retcode = subprocess.call(["bin/nova-manage", "instance_type", + "delete", self.name], stdout=self.fnull) + self.assertEqual(0, retcode) + retcode = subprocess.call(["bin/nova-manage", "instance_type", + "delete", self.name, "--purge"], + stdout=self.fnull) self.assertEqual(0, retcode) def test_list_instance_types_or_flavors(self): - fnull = open(os.devnull, 'w') for c in ["instance_type", "flavor"]: retcode = subprocess.call(["bin/nova-manage", c, \ - "list"], stdout=fnull) + "list"], stdout=self.fnull) self.assertEqual(0, retcode) def test_list_specific_instance_type(self): - fnull = open(os.devnull, 'w') retcode = subprocess.call(["bin/nova-manage", "instance_type", "list", - "m1.medium"], stdout=fnull) + "m1.medium"], stdout=self.fnull) self.assertEqual(0, retcode) def test_should_error_on_bad_create_args(self): - fnull = open(os.devnull, 'w') # shouldn't be able to create instance type with 0 vcpus - retcode = subprocess.call(["bin/nova-manage", "instance_type",\ - "create", self.name, "256", "0",\ - "120", self.flavorid], stdout=fnull) + retcode = subprocess.call(["bin/nova-manage", "instance_type", + "create", self.name + "bad_args", + "256", "0", "120", self.flavorid], + stdout=self.fnull) self.assertEqual(1, retcode) def test_should_fail_on_duplicate_flavorid(self): - fnull = open(os.devnull, 'w') # flavorid 1 is set in migration seed data retcode = subprocess.call(["bin/nova-manage", "instance_type",\ - "create", self.name, "256", "1",\ - "120", "1"], stdout=fnull) - self.assertEqual(1, retcode) + "create", self.name + "dupflavor", "256", + "1", "120", "1"], stdout=self.fnull) + self.assertEqual(3, retcode) def test_should_fail_on_duplicate_name(self): - # FIXME(ken-pepple) duplicate_name really needs to be unique - duplicate_name = "sdfsdfsafsdfsd" - fnull = open(os.devnull, 'w') + duplicate_name = self.name + "dup_name" retcode = subprocess.call([ "bin/nova-manage", "instance_type", @@ -102,27 +102,29 @@ class NovaManageTestCase(test.TestCase): "2", "10", "10"], - stdout=fnull) + stdout=self.fnull) + self.assertEqual(0, retcode) duplicate_retcode = subprocess.call([ "bin/nova-manage", "instance_type", "create", duplicate_name, - "256", + "512", "1", - "120", - self.flavorid, + "240", + str(int(self.flavorid) + 1), "2", "10", "10"], - stdout=fnull) + stdout=self.fnull) self.assertEqual(3, duplicate_retcode) - delete_retcode = subprocess.call(["bin/nova-manage", "instance_type",\ - "delete", duplicate_name], stdout=fnull) + delete_retcode = subprocess.call(["bin/nova-manage", "instance_type", + "delete", duplicate_name, "--purge"], + stdout=self.fnull) self.assertEqual(0, delete_retcode) def test_instance_type_delete_should_fail_without_valid_name(self): - fnull = open(os.devnull, 'w') - retcode = subprocess.call(["bin/nova-manage", "instance_type",\ - "delete", "doesntexist"], stdout=fnull) + retcode = subprocess.call(["bin/nova-manage", "instance_type", + "delete", "doesntexist"], + stdout=self.fnull) self.assertEqual(1, retcode) -- cgit From ac5a1cfb0dbcebd36e7cbaab20795d03d523afee Mon Sep 17 00:00:00 2001 From: Josh Kearney Date: Mon, 21 Feb 2011 11:20:03 -0600 Subject: Stub out VM create --- nova/virt/xenapi/vmops.py | 37 +++++++++++++++++++++++++------------ 1 file changed, 25 insertions(+), 12 deletions(-) diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py index 4f3468f8e..ac09179a3 100644 --- a/nova/virt/xenapi/vmops.py +++ b/nova/virt/xenapi/vmops.py @@ -27,6 +27,7 @@ import tempfile import uuid from nova import db +from nova import compute from nova import context from nova import log as logging from nova import exception @@ -49,6 +50,8 @@ class VMOps(object): def __init__(self, session): self.XenAPI = session.get_imported_xenapi() self._session = session + self.compute_api = compute.API() + VMHelper.XenAPI = self.XenAPI def list_instances(self): @@ -364,21 +367,31 @@ class VMOps(object): def rescue(self, instance, callback): """Rescue the specified instance""" vm = self._get_vm_opaque_ref(instance) - target_vm = VMHelper.lookup(self._session, "instance-00000012") - - self._shutdown(instance, vm) - vbd = self._session.get_xenapi().VM.get_VBDs(vm)[0] - vdi_ref = self._session.get_xenapi().VBD.get_record(vbd)["VDI"] - vbd_ref = VMHelper.create_vbd( - self._session, - target_vm, - vdi_ref, - 1, - False) + #self._shutdown(instance, vm) + #target_vm = VMHelper.lookup(self._session, "instance-00000012") + target_vm = self.compute_api.create( + context=context.get_admin_context(), + instance_type="m1.tiny", + image_id=1, + kernel_id=3, + ramdisk_id=2, + display_name="test", + display_description="test") + print context.get_admin_context().__dict__ + print target_vm + + #vbd = self._session.get_xenapi().VM.get_VBDs(vm)[0] + #vdi_ref = self._session.get_xenapi().VBD.get_record(vbd)["VDI"] + #vbd_ref = VMHelper.create_vbd( + # self._session, + # target_vm, + # vdi_ref, + # 1, + # False) # Plug the VBD into the target instance - self._session.call_xenapi("Async.VBD.plug", vbd_ref) + #self._session.call_xenapi("Async.VBD.plug", vbd_ref) def unrescue(self, instance, callback): """Unrescue the specified instance""" -- cgit From 78ed840ef2f7066c638a76cc3192fec2f93d8450 Mon Sep 17 00:00:00 2001 From: Josh Kearney Date: Mon, 21 Feb 2011 12:48:34 -0600 Subject: Enable rescue testing --- nova/virt/xenapi/vmops.py | 51 ++++++++++++++++++++++++----------------------- 1 file changed, 26 insertions(+), 25 deletions(-) diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py index 3c9fd7d31..bd57dc9a1 100644 --- a/nova/virt/xenapi/vmops.py +++ b/nova/virt/xenapi/vmops.py @@ -406,8 +406,8 @@ class VMOps(object): if ramdisk: args['ramdisk-file'] = ramdisk task2 = self._session.async_call_plugin('glance', fn, args) - self._session.wait_for_task(instance.id, task1) - self._session.wait_for_task(instance.id, task2) + self._session.wait_for_task(task1, instance.id) + self._session.wait_for_task(task2, instance.id) LOG.debug(_("kernel/ramdisk files removed")) except self.XenAPI.Failure, exc: LOG.exception(exc) @@ -477,35 +477,36 @@ class VMOps(object): """Rescue the specified instance""" vm = self._get_vm_opaque_ref(instance) - #self._shutdown(instance, vm) - #target_vm = VMHelper.lookup(self._session, "instance-00000012") - target_vm = self.compute_api.create( - context=context.get_admin_context(), - instance_type="m1.tiny", - image_id=1, - kernel_id=3, - ramdisk_id=2, - display_name="test", - display_description="test") - print context.get_admin_context().__dict__ - print target_vm - - #vbd = self._session.get_xenapi().VM.get_VBDs(vm)[0] - #vdi_ref = self._session.get_xenapi().VBD.get_record(vbd)["VDI"] - #vbd_ref = VMHelper.create_vbd( - # self._session, - # target_vm, - # vdi_ref, - # 1, - # False) + self._shutdown(instance, vm) + target_vm = VMHelper.lookup(self._session, "instance-00000001") + #target_vm = self.compute_api.create( + # context=context.get_admin_context(), + # instance_type="m1.tiny", + # image_id=1, + # kernel_id=3, + # ramdisk_id=2, + # display_name="test", + # display_description="test") + #print context.get_admin_context().__dict__ + #print target_vm + + vbd = self._session.get_xenapi().VM.get_VBDs(vm)[0] + vdi_ref = self._session.get_xenapi().VBD.get_record(vbd)["VDI"] + vbd_ref = VMHelper.create_vbd( + self._session, + target_vm, + vdi_ref, + 1, + False) # Plug the VBD into the target instance - #self._session.call_xenapi("Async.VBD.plug", vbd_ref) + self._session.call_xenapi("Async.VBD.plug", vbd_ref) + pass def unrescue(self, instance, callback): """Unrescue the specified instance""" vm = self._get_vm_opaque_ref(instance) - target_vm = VMHelper.lookup(self._session, "instance-00000012") + target_vm = VMHelper.lookup(self._session, "instance-00000001") vbds = self._session.get_xenapi().VM.get_VBDs(target_vm) -- cgit From 94bd7e2c45ca971e318813bb1e897fb5e79ab7bd Mon Sep 17 00:00:00 2001 From: Josh Kearney Date: Wed, 23 Feb 2011 12:05:46 -0600 Subject: Removed pass --- nova/virt/xenapi/vmops.py | 1 - 1 file changed, 1 deletion(-) diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py index bd57dc9a1..7bffd8c6c 100644 --- a/nova/virt/xenapi/vmops.py +++ b/nova/virt/xenapi/vmops.py @@ -501,7 +501,6 @@ class VMOps(object): # Plug the VBD into the target instance self._session.call_xenapi("Async.VBD.plug", vbd_ref) - pass def unrescue(self, instance, callback): """Unrescue the specified instance""" -- cgit From 3c6a44327f0015cb531b06fbce38eb4e3a04ce6a Mon Sep 17 00:00:00 2001 From: Ricardo Carrillo Cruz Date: Wed, 23 Feb 2011 20:13:50 +0100 Subject: Fixes lp715424, code now checks network range can fit num_networks * network_size --- nova/network/manager.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/nova/network/manager.py b/nova/network/manager.py index c6eba225e..1b8627562 100644 --- a/nova/network/manager.py +++ b/nova/network/manager.py @@ -521,6 +521,11 @@ class VlanManager(NetworkManager): ' than 4094')) fixed_net = IPy.IP(cidr) + if fixed_net.len() < num_networks * network_size: + raise ValueError(_('The network range is not big enough to fit %s' + ' networks of size %s' % + (num_networks, network_size))) + fixed_net_v6 = IPy.IP(cidr_v6) network_size_v6 = 1 << 64 significant_bits_v6 = 64 -- cgit From 7c19fe87693b89d56ef9d9402d2667eacf297b97 Mon Sep 17 00:00:00 2001 From: Ricardo Carrillo Cruz Date: Wed, 23 Feb 2011 20:51:27 +0100 Subject: Deleted trailing whitespace --- nova/network/manager.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nova/network/manager.py b/nova/network/manager.py index 1b8627562..7bdd56d7b 100644 --- a/nova/network/manager.py +++ b/nova/network/manager.py @@ -523,7 +523,7 @@ class VlanManager(NetworkManager): fixed_net = IPy.IP(cidr) if fixed_net.len() < num_networks * network_size: raise ValueError(_('The network range is not big enough to fit %s' - ' networks of size %s' % + ' networks of size %s' % (num_networks, network_size))) fixed_net_v6 = IPy.IP(cidr_v6) -- cgit From 9e018bbee1d4a308fdf1700bd25aa733cedc26e6 Mon Sep 17 00:00:00 2001 From: Josh Kearney Date: Thu, 24 Feb 2011 13:01:16 -0600 Subject: Updated email in Authors --- .mailmap | 1 + Authors | 2 +- nova/virt/xenapi/vmops.py | 20 +++++++++----------- 3 files changed, 11 insertions(+), 12 deletions(-) diff --git a/.mailmap b/.mailmap index a839eba6c..d479bfdf2 100644 --- a/.mailmap +++ b/.mailmap @@ -19,6 +19,7 @@ + Masumoto diff --git a/Authors b/Authors index 494e614a0..39241b45c 100644 --- a/Authors +++ b/Authors @@ -31,7 +31,7 @@ John Dewey Jonathan Bryce Jordan Rinke Josh Durgin -Josh Kearney +Josh Kearney Joshua McKenty Justin Santa Barbara Kei Masumoto diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py index 7bffd8c6c..a47d23f88 100644 --- a/nova/virt/xenapi/vmops.py +++ b/nova/virt/xenapi/vmops.py @@ -476,19 +476,15 @@ class VMOps(object): def rescue(self, instance, callback): """Rescue the specified instance""" vm = self._get_vm_opaque_ref(instance) - self._shutdown(instance, vm) + + # Temporary instance target_vm = VMHelper.lookup(self._session, "instance-00000001") - #target_vm = self.compute_api.create( - # context=context.get_admin_context(), - # instance_type="m1.tiny", - # image_id=1, - # kernel_id=3, - # ramdisk_id=2, - # display_name="test", - # display_description="test") - #print context.get_admin_context().__dict__ - #print target_vm + + # Plan of action + # Create `instance` object for rescue_vm + # rescue_instance = self._make_rescue_instance() # Write this + #rescue_vm = self.spawn(instance) vbd = self._session.get_xenapi().VM.get_VBDs(vm)[0] vdi_ref = self._session.get_xenapi().VBD.get_record(vbd)["VDI"] @@ -505,6 +501,8 @@ class VMOps(object): def unrescue(self, instance, callback): """Unrescue the specified instance""" vm = self._get_vm_opaque_ref(instance) + + # Temporary instance target_vm = VMHelper.lookup(self._session, "instance-00000001") vbds = self._session.get_xenapi().VM.get_VBDs(target_vm) -- cgit From bf222b9173e0a5a0bfbcf4705caab390ee33334b Mon Sep 17 00:00:00 2001 From: Ken Pepple Date: Thu, 24 Feb 2011 11:17:42 -0800 Subject: moved migrate script to 007 (again..sigh) --- .../versions/006_add_instance_types.py | 86 ---------------------- .../versions/007_add_instance_types.py | 86 ++++++++++++++++++++++ 2 files changed, 86 insertions(+), 86 deletions(-) delete mode 100644 nova/db/sqlalchemy/migrate_repo/versions/006_add_instance_types.py create mode 100644 nova/db/sqlalchemy/migrate_repo/versions/007_add_instance_types.py diff --git a/nova/db/sqlalchemy/migrate_repo/versions/006_add_instance_types.py b/nova/db/sqlalchemy/migrate_repo/versions/006_add_instance_types.py deleted file mode 100644 index fec191214..000000000 --- a/nova/db/sqlalchemy/migrate_repo/versions/006_add_instance_types.py +++ /dev/null @@ -1,86 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# 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. - -from sqlalchemy import * -from migrate import * - -from nova import api -from nova import db -from nova import log as logging - -import datetime - -meta = MetaData() - - -# -# New Tables -# -instance_types = Table('instance_types', meta, - Column('created_at', DateTime(timezone=False)), - Column('updated_at', DateTime(timezone=False)), - Column('deleted_at', DateTime(timezone=False)), - Column('deleted', Boolean(create_constraint=True, name=None)), - Column('name', - String(length=255, convert_unicode=False, assert_unicode=None, - unicode_error=None, _warn_on_bytestring=False), - unique=True), - Column('id', Integer(), primary_key=True, nullable=False), - Column('memory_mb', Integer(), nullable=False), - Column('vcpus', Integer(), nullable=False), - Column('local_gb', Integer(), nullable=False), - Column('flavorid', Integer(), nullable=False, unique=True), - Column('swap', Integer(), nullable=False, default=0), - Column('rxtx_quota', Integer(), nullable=False, default=0), - Column('rxtx_cap', Integer(), nullable=False, default=0)) - - -def upgrade(migrate_engine): - # Upgrade operations go here - # Don't create your own engine; bind migrate_engine - # to your metadata - meta.bind = migrate_engine - try: - instance_types.create() - except Exception: - logging.info(repr(table)) - logging.exception('Exception while creating instance_types table') - raise - - # Here are the old static instance types - INSTANCE_TYPES = { - 'm1.tiny': dict(memory_mb=512, vcpus=1, local_gb=0, flavorid=1), - 'm1.small': dict(memory_mb=2048, vcpus=1, local_gb=20, flavorid=2), - 'm1.medium': dict(memory_mb=4096, vcpus=2, local_gb=40, flavorid=3), - 'm1.large': dict(memory_mb=8192, vcpus=4, local_gb=80, flavorid=4), - 'm1.xlarge': dict(memory_mb=16384, vcpus=8, local_gb=160, flavorid=5)} - try: - i = instance_types.insert() - for name, values in INSTANCE_TYPES.iteritems(): - # FIXME(kpepple) should we be seeding created_at / updated_at ? - # now = datetime.datatime.utcnow() - i.execute({'name': name, 'memory_mb': values["memory_mb"], - 'vcpus': values["vcpus"], 'deleted': 0, - 'local_gb': values["local_gb"], - 'flavorid': values["flavorid"]}) - except Exception: - logging.info(repr(table)) - logging.exception('Exception while seeding instance_types table') - raise - - -def downgrade(migrate_engine): - # Operations to reverse the above upgrade go here. - for table in (instance_types): - table.drop() diff --git a/nova/db/sqlalchemy/migrate_repo/versions/007_add_instance_types.py b/nova/db/sqlalchemy/migrate_repo/versions/007_add_instance_types.py new file mode 100644 index 000000000..fec191214 --- /dev/null +++ b/nova/db/sqlalchemy/migrate_repo/versions/007_add_instance_types.py @@ -0,0 +1,86 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# 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. + +from sqlalchemy import * +from migrate import * + +from nova import api +from nova import db +from nova import log as logging + +import datetime + +meta = MetaData() + + +# +# New Tables +# +instance_types = Table('instance_types', meta, + Column('created_at', DateTime(timezone=False)), + Column('updated_at', DateTime(timezone=False)), + Column('deleted_at', DateTime(timezone=False)), + Column('deleted', Boolean(create_constraint=True, name=None)), + Column('name', + String(length=255, convert_unicode=False, assert_unicode=None, + unicode_error=None, _warn_on_bytestring=False), + unique=True), + Column('id', Integer(), primary_key=True, nullable=False), + Column('memory_mb', Integer(), nullable=False), + Column('vcpus', Integer(), nullable=False), + Column('local_gb', Integer(), nullable=False), + Column('flavorid', Integer(), nullable=False, unique=True), + Column('swap', Integer(), nullable=False, default=0), + Column('rxtx_quota', Integer(), nullable=False, default=0), + Column('rxtx_cap', Integer(), nullable=False, default=0)) + + +def upgrade(migrate_engine): + # Upgrade operations go here + # Don't create your own engine; bind migrate_engine + # to your metadata + meta.bind = migrate_engine + try: + instance_types.create() + except Exception: + logging.info(repr(table)) + logging.exception('Exception while creating instance_types table') + raise + + # Here are the old static instance types + INSTANCE_TYPES = { + 'm1.tiny': dict(memory_mb=512, vcpus=1, local_gb=0, flavorid=1), + 'm1.small': dict(memory_mb=2048, vcpus=1, local_gb=20, flavorid=2), + 'm1.medium': dict(memory_mb=4096, vcpus=2, local_gb=40, flavorid=3), + 'm1.large': dict(memory_mb=8192, vcpus=4, local_gb=80, flavorid=4), + 'm1.xlarge': dict(memory_mb=16384, vcpus=8, local_gb=160, flavorid=5)} + try: + i = instance_types.insert() + for name, values in INSTANCE_TYPES.iteritems(): + # FIXME(kpepple) should we be seeding created_at / updated_at ? + # now = datetime.datatime.utcnow() + i.execute({'name': name, 'memory_mb': values["memory_mb"], + 'vcpus': values["vcpus"], 'deleted': 0, + 'local_gb': values["local_gb"], + 'flavorid': values["flavorid"]}) + except Exception: + logging.info(repr(table)) + logging.exception('Exception while seeding instance_types table') + raise + + +def downgrade(migrate_engine): + # Operations to reverse the above upgrade go here. + for table in (instance_types): + table.drop() -- cgit From 907df1b90e5c22eb82ce85bc12f48d8abf09b665 Mon Sep 17 00:00:00 2001 From: Josh Kearney Date: Thu, 24 Feb 2011 13:57:11 -0600 Subject: nothing --- nova/virt/xenapi/vmops.py | 40 ++++++++++++++++------------------------ 1 file changed, 16 insertions(+), 24 deletions(-) diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py index a47d23f88..0b9b7d0a4 100644 --- a/nova/virt/xenapi/vmops.py +++ b/nova/virt/xenapi/vmops.py @@ -475,37 +475,29 @@ class VMOps(object): def rescue(self, instance, callback): """Rescue the specified instance""" - vm = self._get_vm_opaque_ref(instance) - self._shutdown(instance, vm) - - # Temporary instance - target_vm = VMHelper.lookup(self._session, "instance-00000001") + #vm = self._get_vm_opaque_ref(instance) + #self._shutdown(instance, vm) - # Plan of action - # Create `instance` object for rescue_vm - # rescue_instance = self._make_rescue_instance() # Write this + print instance.__dict__ + print instance.name + print "%s-rescue" % instance.name #rescue_vm = self.spawn(instance) - vbd = self._session.get_xenapi().VM.get_VBDs(vm)[0] - vdi_ref = self._session.get_xenapi().VBD.get_record(vbd)["VDI"] - vbd_ref = VMHelper.create_vbd( - self._session, - target_vm, - vdi_ref, - 1, - False) + #vbd = self._session.get_xenapi().VM.get_VBDs(vm)[0] + #vdi_ref = self._session.get_xenapi().VBD.get_record(vbd)["VDI"] + #vbd_ref = VMHelper.create_vbd( + # self._session, + # rescue_vm, + # vdi_ref, + # 1, + # False) - # Plug the VBD into the target instance - self._session.call_xenapi("Async.VBD.plug", vbd_ref) + #self._session.call_xenapi("Async.VBD.plug", vbd_ref) def unrescue(self, instance, callback): """Unrescue the specified instance""" vm = self._get_vm_opaque_ref(instance) - - # Temporary instance - target_vm = VMHelper.lookup(self._session, "instance-00000001") - - vbds = self._session.get_xenapi().VM.get_VBDs(target_vm) + vbds = self._session.get_xenapi().VM.get_VBDs(vm) for vbd_ref in vbds: vbd = self._session.get_xenapi().VBD.get_record(vbd_ref) @@ -513,7 +505,7 @@ class VMOps(object): VMHelper.unplug_vbd(self._session, vbd_ref) VMHelper.destroy_vbd(self._session, vbd_ref) - self._start(instance, vm) + self.reboot(instance) def get_info(self, instance): """Return data about VM instance""" -- cgit From 6033f657aad9bd5b244a21908caedfc92840c9cf Mon Sep 17 00:00:00 2001 From: Josh Kearney Date: Fri, 25 Feb 2011 10:54:37 -0600 Subject: Create rescue instance --- nova/db/sqlalchemy/models.py | 7 ++++- nova/virt/xenapi/vmops.py | 69 +++++++++++++++++++++++++------------------- 2 files changed, 45 insertions(+), 31 deletions(-) diff --git a/nova/db/sqlalchemy/models.py b/nova/db/sqlalchemy/models.py index 1882efeba..b1eb1a7b7 100644 --- a/nova/db/sqlalchemy/models.py +++ b/nova/db/sqlalchemy/models.py @@ -126,11 +126,16 @@ class Certificate(BASE, NovaBase): class Instance(BASE, NovaBase): """Represents a guest vm.""" __tablename__ = 'instances' + onset_files = [] + id = Column(Integer, primary_key=True, autoincrement=True) @property def name(self): - return FLAGS.instance_name_template % self.id + base_name = FLAGS.instance_name_template % self.id + if getattr(self, '_rescue', False): + base_name += "-rescue" + return base_name admin_pass = Column(String(255)) user_id = Column(String(255)) diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py index 0b9b7d0a4..d70129d80 100644 --- a/nova/virt/xenapi/vmops.py +++ b/nova/virt/xenapi/vmops.py @@ -65,20 +65,20 @@ class VMOps(object): def spawn(self, instance): """Create VM instance""" - vm = VMHelper.lookup(self._session, instance.name) + instance_name = instance.name + vm = VMHelper.lookup(self._session, instance_name) if vm is not None: raise exception.Duplicate(_('Attempted to create' - ' non-unique name %s') % instance.name) + ' non-unique name %s') % instance_name) #ensure enough free memory is available if not VMHelper.ensure_free_mem(self._session, instance): - name = instance['name'] - LOG.exception(_('instance %(name)s: not enough free memory') - % locals()) - db.instance_set_state(context.get_admin_context(), - instance['id'], - power_state.SHUTDOWN) - return + LOG.exception(_('instance %(instance_name)s: not enough free ' + 'memory') % locals()) + db.instance_set_state(context.get_admin_context(), + instance['id'], + power_state.SHUTDOWN) + return user = AuthManager().get_user(instance.user_id) project = AuthManager().get_project(instance.project_id) @@ -150,9 +150,8 @@ class VMOps(object): LOG.debug(_('Starting VM %s...'), vm_ref) self._session.call_xenapi('VM.start', vm_ref, False, False) - instance_name = instance.name LOG.info(_('Spawning VM %(instance_name)s created %(vm_ref)s.') - % locals()) + % locals()) def _inject_onset_files(): onset_files = instance.onset_files @@ -176,18 +175,18 @@ class VMOps(object): def _wait_for_boot(): try: - state = self.get_info(instance['name'])['state'] + state = self.get_info(instance_name)['state'] db.instance_set_state(context.get_admin_context(), instance['id'], state) if state == power_state.RUNNING: - LOG.debug(_('Instance %s: booted'), instance['name']) + LOG.debug(_('Instance %s: booted'), instance_name) timer.stop() _inject_onset_files() return True except Exception, exc: LOG.warn(exc) LOG.exception(_('instance %s: failed to boot'), - instance['name']) + instance_name) db.instance_set_state(context.get_admin_context(), instance['id'], power_state.SHUTDOWN) @@ -475,24 +474,26 @@ class VMOps(object): def rescue(self, instance, callback): """Rescue the specified instance""" - #vm = self._get_vm_opaque_ref(instance) - #self._shutdown(instance, vm) + vm = self._get_vm_opaque_ref(instance) + self._shutdown(instance, vm) - print instance.__dict__ - print instance.name - print "%s-rescue" % instance.name - #rescue_vm = self.spawn(instance) + # log old instance + # log new instance - #vbd = self._session.get_xenapi().VM.get_VBDs(vm)[0] - #vdi_ref = self._session.get_xenapi().VBD.get_record(vbd)["VDI"] - #vbd_ref = VMHelper.create_vbd( - # self._session, - # rescue_vm, - # vdi_ref, - # 1, - # False) + instance._rescue = True + self.spawn(instance) + rescue_vm = self._get_vm_opaque_ref(instance) - #self._session.call_xenapi("Async.VBD.plug", vbd_ref) + vbd = self._session.get_xenapi().VM.get_VBDs(vm)[0] + vdi_ref = self._session.get_xenapi().VBD.get_record(vbd)["VDI"] + vbd_ref = VMHelper.create_vbd( + self._session, + rescue_vm, + vdi_ref, + 1, + False) + + self._session.call_xenapi("Async.VBD.plug", vbd_ref) def unrescue(self, instance, callback): """Unrescue the specified instance""" @@ -505,7 +506,15 @@ class VMOps(object): VMHelper.unplug_vbd(self._session, vbd_ref) VMHelper.destroy_vbd(self._session, vbd_ref) - self.reboot(instance) + # fetch old instance + # fetch new instance + # destroy new instance + # start old instance + + self.destroy(instance) + instance._rescue = False + + self._start(instance, vm) def get_info(self, instance): """Return data about VM instance""" -- cgit From 2dc9d8bfe4bcd61c4d634767715b2be3a214426e Mon Sep 17 00:00:00 2001 From: Josh Kearney Date: Fri, 25 Feb 2011 12:43:09 -0600 Subject: Teardown rescue instance --- nova/virt/xenapi/vmops.py | 34 +++++++++++++++++++++------------- 1 file changed, 21 insertions(+), 13 deletions(-) diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py index 93c2ab0c7..6b531ca2c 100644 --- a/nova/virt/xenapi/vmops.py +++ b/nova/virt/xenapi/vmops.py @@ -113,7 +113,7 @@ class VMOps(object): self.create_vifs(instance, networks) LOG.debug(_('Starting VM %s...'), vm_ref) - self._session.call_xenapi('VM.start', vm_ref, False, False) + self._start(instance, vm_ref) LOG.info(_('Spawning VM %(instance_name)s created %(vm_ref)s.') % locals()) @@ -438,12 +438,14 @@ class VMOps(object): def rescue(self, instance, callback): """Rescue the specified instance""" + rescue_vm = VMHelper.lookup(self._session, instance.name + "-rescue") + if rescue_vm: + raise RuntimeError(_( + "Instance is already in Rescue Mode: %s" % instance.name)) + vm = self._get_vm_opaque_ref(instance) self._shutdown(instance, vm) - # log old instance - # log new instance - instance._rescue = True self.spawn(instance) rescue_vm = self._get_vm_opaque_ref(instance) @@ -461,8 +463,16 @@ class VMOps(object): def unrescue(self, instance, callback): """Unrescue the specified instance""" - vm = self._get_vm_opaque_ref(instance) - vbds = self._session.get_xenapi().VM.get_VBDs(vm) + rescue_vm = VMHelper.lookup(self._session, instance.name + "-rescue") + + if not rescue_vm: + raise exception.NotFound(_( + "Instance is not in Rescue Mode: %s" % instance.name)) + + original_vm = self._get_vm_opaque_ref(instance) + vbds = self._session.get_xenapi().VM.get_VBDs(rescue_vm) + + instance._rescue = False for vbd_ref in vbds: vbd = self._session.get_xenapi().VBD.get_record(vbd_ref) @@ -470,15 +480,13 @@ class VMOps(object): VMHelper.unplug_vbd(self._session, vbd_ref) VMHelper.destroy_vbd(self._session, vbd_ref) - # fetch old instance - # fetch new instance - # destroy new instance - # start old instance + task1 = self._session.call_xenapi("Async.VM.hard_shutdown", rescue_vm) + self._session.wait_for_task(task1, instance.id) - self.destroy(instance) - instance._rescue = False + task2 = self._session.call_xenapi('Async.VM.destroy', rescue_vm) + self._session.wait_for_task(task2, instance.id) - self._start(instance, vm) + self._start(instance, original_vm) def get_info(self, instance): """Return data about VM instance""" -- cgit From 6cfa479a1375fb8274dd9130946eab38e1398538 Mon Sep 17 00:00:00 2001 From: Josh Kearney Date: Fri, 25 Feb 2011 14:35:29 -0600 Subject: Set rescue instance VIF device --- nova/virt/xenapi/vm_utils.py | 4 ++-- nova/virt/xenapi/vmops.py | 21 +++++++++++++++++++-- 2 files changed, 21 insertions(+), 4 deletions(-) diff --git a/nova/virt/xenapi/vm_utils.py b/nova/virt/xenapi/vm_utils.py index 206201817..f0b1e993c 100644 --- a/nova/virt/xenapi/vm_utils.py +++ b/nova/virt/xenapi/vm_utils.py @@ -207,11 +207,11 @@ class VMHelper(HelperBase): raise StorageError(_('Unable to destroy VBD %s') % vbd_ref) @classmethod - def create_vif(cls, session, vm_ref, network_ref, mac_address): + def create_vif(cls, session, vm_ref, network_ref, mac_address, dev="0"): """Create a VIF record. Returns a Deferred that gives the new VIF reference.""" vif_rec = {} - vif_rec['device'] = '0' + vif_rec['device'] = dev vif_rec['network'] = network_ref vif_rec['VM'] = vm_ref vif_rec['MAC'] = mac_address diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py index 6b531ca2c..c1e9bec62 100644 --- a/nova/virt/xenapi/vmops.py +++ b/nova/virt/xenapi/vmops.py @@ -483,6 +483,14 @@ class VMOps(object): task1 = self._session.call_xenapi("Async.VM.hard_shutdown", rescue_vm) self._session.wait_for_task(task1, instance.id) + vdis = VMHelper.lookup_vm_vdis(self._session, rescue_vm) + for vdi in vdis: + try: + task = self._session.call_xenapi('Async.VDI.destroy', vdi) + self._session.wait_for_task(task, instance.id) + except self.XenAPI.Failure: + continue + task2 = self._session.call_xenapi('Async.VM.destroy', rescue_vm) self._session.wait_for_task(task2, instance.id) @@ -574,8 +582,17 @@ class VMOps(object): NetworkHelper.find_network_with_bridge(self._session, bridge) if network_ref: - VMHelper.create_vif(self._session, vm_opaque_ref, - network_ref, instance.mac_address) + try: + device = "1" if instance._rescue else "0" + except AttributeError: + device = "0" + + VMHelper.create_vif( + self._session, + vm_opaque_ref, + network_ref, + instance.mac_address, + device) def reset_network(self, instance): """ -- cgit From 7ad2e0a731e6b2fbace8528439f6a8c2f0d5aaad Mon Sep 17 00:00:00 2001 From: Josh Kearney Date: Fri, 25 Feb 2011 14:47:25 -0600 Subject: Removed unnecessary compute import --- nova/virt/xenapi/vmops.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py index c1e9bec62..3e3c9ff6e 100644 --- a/nova/virt/xenapi/vmops.py +++ b/nova/virt/xenapi/vmops.py @@ -27,7 +27,6 @@ import tempfile import uuid from nova import db -from nova import compute from nova import context from nova import log as logging from nova import exception @@ -50,7 +49,6 @@ class VMOps(object): def __init__(self, session): self.XenAPI = session.get_imported_xenapi() self._session = session - self.compute_api = compute.API() VMHelper.XenAPI = self.XenAPI -- cgit From 97566c04b3a04626f1bc1b66e72c61b4621b6c7d Mon Sep 17 00:00:00 2001 From: Josh Kearney Date: Fri, 25 Feb 2011 16:18:11 -0600 Subject: Bootlock original instance during rescue --- nova/virt/xenapi/vmops.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py index 3e3c9ff6e..21477a18c 100644 --- a/nova/virt/xenapi/vmops.py +++ b/nova/virt/xenapi/vmops.py @@ -196,6 +196,19 @@ class VMOps(object): _('Instance not present %s') % instance_name) return vm + def _bootlock(self, vm, unlock=False): + """Prevent an instance from booting""" + if unlock: + self._session.call_xenapi( + "VM.remove_from_blocked_operations", + vm, + "start") + else: + self._session.call_xenapi( + "VM.set_blocked_operations", + vm, + {"start": ""}) + def snapshot(self, instance, image_id): """ Create snapshot from a running VM instance @@ -443,6 +456,7 @@ class VMOps(object): vm = self._get_vm_opaque_ref(instance) self._shutdown(instance, vm) + self._bootlock(vm) instance._rescue = True self.spawn(instance) @@ -492,6 +506,7 @@ class VMOps(object): task2 = self._session.call_xenapi('Async.VM.destroy', rescue_vm) self._session.wait_for_task(task2, instance.id) + self._bootlock(original_vm, unlock=True) self._start(instance, original_vm) def get_info(self, instance): -- cgit From 7a6daa8d92f4f11fe2fce8fb2f4b11d96cb98c2d Mon Sep 17 00:00:00 2001 From: "Kevin L. Mitchell" Date: Mon, 28 Feb 2011 17:27:19 +0000 Subject: Suppress stack traces unless --verbose is specified --- nova/log.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/nova/log.py b/nova/log.py index 87a21ddb4..d194ab8f0 100644 --- a/nova/log.py +++ b/nova/log.py @@ -266,7 +266,10 @@ class NovaRootLogger(NovaLogger): def handle_exception(type, value, tb): - logging.root.critical(str(value), exc_info=(type, value, tb)) + extra = {} + if FLAGS.verbose: + extra['exc_info'] = (type, value, tb) + logging.root.critical(str(value), **extra) def reset(): -- cgit From c1bcf1dead8734a02172b4ac20b24fbbb7dbb993 Mon Sep 17 00:00:00 2001 From: Cerberus Date: Mon, 28 Feb 2011 11:40:22 -0600 Subject: Rename migration to coincide with latest trunk changes --- .../versions/004_add_instance_migrations.py | 61 ---------------------- .../versions/007_add_instance_migrations.py | 61 ++++++++++++++++++++++ 2 files changed, 61 insertions(+), 61 deletions(-) delete mode 100644 nova/db/sqlalchemy/migrate_repo/versions/004_add_instance_migrations.py create mode 100644 nova/db/sqlalchemy/migrate_repo/versions/007_add_instance_migrations.py diff --git a/nova/db/sqlalchemy/migrate_repo/versions/004_add_instance_migrations.py b/nova/db/sqlalchemy/migrate_repo/versions/004_add_instance_migrations.py deleted file mode 100644 index 4fda525f1..000000000 --- a/nova/db/sqlalchemy/migrate_repo/versions/004_add_instance_migrations.py +++ /dev/null @@ -1,61 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright 2010 OpenStack LLC. -# 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.from sqlalchemy import * - -from sqlalchemy import * -from migrate import * - -from nova import log as logging - - -meta = MetaData() - -# Just for the ForeignKey and column creation to succeed, these are not the -# actual definitions of instances or services. -instances = Table('instances', meta, - Column('id', Integer(), primary_key=True, nullable=False), - ) - -# -# New Tables -# - -migrations = Table('migrations', meta, - Column('created_at', DateTime(timezone=False)), - Column('updated_at', DateTime(timezone=False)), - Column('deleted_at', DateTime(timezone=False)), - Column('deleted', Boolean(create_constraint=True, name=None)), - Column('id', Integer(), primary_key=True, nullable=False), - Column('source_compute', String(255)), - Column('dest_compute', String(255)), - Column('dest_host', String(255)), - Column('instance_id', Integer, ForeignKey('instances.id'), - nullable=True), - Column('status', String(255)), - ) - - -def upgrade(migrate_engine): - # Upgrade operations go here. Don't create your own engine; - # bind migrate_engine to your metadata - meta.bind = migrate_engine - for table in (migrations, ): - try: - table.create() - except Exception: - logging.info(repr(table)) - logging.exception('Exception while creating table') - raise diff --git a/nova/db/sqlalchemy/migrate_repo/versions/007_add_instance_migrations.py b/nova/db/sqlalchemy/migrate_repo/versions/007_add_instance_migrations.py new file mode 100644 index 000000000..4fda525f1 --- /dev/null +++ b/nova/db/sqlalchemy/migrate_repo/versions/007_add_instance_migrations.py @@ -0,0 +1,61 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright 2010 OpenStack LLC. +# 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.from sqlalchemy import * + +from sqlalchemy import * +from migrate import * + +from nova import log as logging + + +meta = MetaData() + +# Just for the ForeignKey and column creation to succeed, these are not the +# actual definitions of instances or services. +instances = Table('instances', meta, + Column('id', Integer(), primary_key=True, nullable=False), + ) + +# +# New Tables +# + +migrations = Table('migrations', meta, + Column('created_at', DateTime(timezone=False)), + Column('updated_at', DateTime(timezone=False)), + Column('deleted_at', DateTime(timezone=False)), + Column('deleted', Boolean(create_constraint=True, name=None)), + Column('id', Integer(), primary_key=True, nullable=False), + Column('source_compute', String(255)), + Column('dest_compute', String(255)), + Column('dest_host', String(255)), + Column('instance_id', Integer, ForeignKey('instances.id'), + nullable=True), + Column('status', String(255)), + ) + + +def upgrade(migrate_engine): + # Upgrade operations go here. Don't create your own engine; + # bind migrate_engine to your metadata + meta.bind = migrate_engine + for table in (migrations, ): + try: + table.create() + except Exception: + logging.info(repr(table)) + logging.exception('Exception while creating table') + raise -- cgit From 8da6796789767b1341cb5a650066b67ad3191c74 Mon Sep 17 00:00:00 2001 From: Cerberus Date: Mon, 28 Feb 2011 12:30:02 -0600 Subject: Merge review fixes --- nova/virt/xenapi/vm_utils.py | 17 ++++-- nova/virt/xenapi/vmops.py | 27 ++++++---- .../xenserver/xenapi/etc/xapi.d/plugins/migration | 62 +++++----------------- 3 files changed, 45 insertions(+), 61 deletions(-) diff --git a/nova/virt/xenapi/vm_utils.py b/nova/virt/xenapi/vm_utils.py index e7ad1f686..870660dea 100644 --- a/nova/virt/xenapi/vm_utils.py +++ b/nova/virt/xenapi/vm_utils.py @@ -300,6 +300,13 @@ class VMHelper(HelperBase): the UUID """ return session.call_xenapi('SR.get_by_name_label', sr_label)[0] + @classmethod + def get_sr_path(cls, session, sr_label='slices'): + """ Finds the SR and then coerces it into a path on the dom0 file + system """ + # TODO(mdietz): replace this with the flag once unified-images merges + return '/var/run/sr-mount/%s' % cls.get_sr(session, sr_label) + @classmethod def upload_image(cls, session, instance_id, vdi_uuids, image_id): """ Requests that the Glance plugin bundle the specified VDIs and @@ -508,13 +515,17 @@ class VMHelper(HelperBase): @classmethod def scan_sr(cls, session, instance_id=None, sr_ref=None): + """Scans the SR specified by sr_ref""" if sr_ref: LOG.debug(_("Re-scanning SR %s"), sr_ref) task = session.call_xenapi('Async.SR.scan', sr_ref) session.wait_for_task(instance_id, task) - else: - sr_ref = cls.get_sr(session) - session.call_xenapi('SR.scan', sr_ref) + + @classmethod + def scan_default_sr(cls, session): + """Looks for the system default SR and triggers a re-scan""" + sr_ref = cls.get_sr(session) + session.call_xenapi('SR.scan', sr_ref) def get_rrd(host, uuid): diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py index f5278ff07..b3e5627d8 100644 --- a/nova/virt/xenapi/vmops.py +++ b/nova/virt/xenapi/vmops.py @@ -298,8 +298,10 @@ class VMOps(object): VMHelper.get_vdi_for_vm_safely(self._session, vm_ref) cow_uuid = vm_vdi_rec['uuid'] - params = {'host': dest, 'vdi_uuid': base_copy_uuid, - 'instance_id': instance.id, } + params = {'host': dest, + 'vdi_uuid': base_copy_uuid, + 'instance_id': instance.id, + 'sr_path': VMHelper.get_sr_path(self._session), } task = self._session.async_call_plugin('migration', 'transfer_vhd', {'params': pickle.dumps(params)}) @@ -308,8 +310,11 @@ class VMOps(object): # Now power down the instance and transfer the COW VHD self._shutdown(instance, vm_ref, method='clean') - params = {'host': dest, 'vdi_uuid': cow_uuid, - 'instance_id': instance.id, } + params = {'host': dest, + 'vdi_uuid': cow_uuid, + 'instance_id': instance.id, + 'sr_path': VMHelper.get_sr_path(self._session), } + task = self._session.async_call_plugin('migration', 'transfer_vhd', {'params': pickle.dumps(params)}) self._session.wait_for_task(instance.id, task) @@ -326,14 +331,15 @@ class VMOps(object): 'old_base_copy_uuid': disk_info['base_copy'], 'old_cow_uuid': disk_info['cow'], 'new_base_copy_uuid': new_base_copy_uuid, - 'new_cow_uuid': new_cow_uuid, } + 'new_cow_uuid': new_cow_uuid, + 'sr_path': VMHelper.get_sr_path(self._session), } task = self._session.async_call_plugin('migration', 'move_vhds_into_sr', {'params': pickle.dumps(params)}) self._session.wait_for_task(instance.id, task) # Now we rescan the SR so we find the VHDs - VMHelper.scan_sr(self._session) + VMHelper.scan_default_sr(self._session) return new_cow_uuid @@ -411,7 +417,7 @@ class VMOps(object): raise RuntimeError(resp_dict['message']) return resp_dict['message'] - def _shutdown(self, instance, vm, method='hard'): + def _shutdown(self, instance, vm, hard=True): """Shutdown an instance """ state = self.get_info(instance['name'])['state'] if state == power_state.SHUTDOWN: @@ -421,10 +427,11 @@ class VMOps(object): try: task = None - if method == 'clean': - task = self._session.call_xenapi('Async.VM.clean_shutdown', vm) - else: + if hard: task = self._session.call_xenapi('Async.VM.hard_shutdown', vm) + else: + task = self._session.call_xenapi('Async.VM.clean_shutdown', vm) + self._session.wait_for_task(instance.id, task) except self.XenAPI.Failure, exc: LOG.exception(exc) diff --git a/plugins/xenserver/xenapi/etc/xapi.d/plugins/migration b/plugins/xenserver/xenapi/etc/xapi.d/plugins/migration index 7a6eefda2..4aa89863a 100644 --- a/plugins/xenserver/xenapi/etc/xapi.d/plugins/migration +++ b/plugins/xenserver/xenapi/etc/xapi.d/plugins/migration @@ -1,7 +1,6 @@ #!/usr/bin/env python -# Copyright 2010 United States Government as represented by the -# Administrator of the National Aeronautics and Space Administration. +# Copyright 2010 OpenStack LLC. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may @@ -31,38 +30,6 @@ import XenAPIPlugin from pluginlib_nova import * configure_logging('migration') -SSH_HOSTS = '/root/.ssh/known_hosts' -DEVNULL = '/dev/null' -KEYSCAN = '/usr/bin/ssh-keyscan' -RSYNC = '/usr/bin/rsync' -FILE_SR_PATH = '/var/run/sr-mount' -IMAGE_PATH = '/images/' -VHD_UTIL = '/usr/sbin/vhd-util' - -def get_sr_path(session): - sr_ref = find_sr(session) - - if sr_ref is None: - raise Exception('Cannot find SR to read VDI from') - - sr_rec = session.xenapi.SR.get_record(sr_ref) - sr_uuid = sr_rec["uuid"] - sr_path = os.path.join(FILE_SR_PATH, sr_uuid) - return sr_path - -def find_sr(session): - host = get_this_host(session) - srs = session.xenapi.SR.get_all() - for sr in srs: - sr_rec = session.xenapi.SR.get_record(sr) - if not ('i18n-key' in sr_rec['other_config'] and - sr_rec['other_config']['i18n-key'] == 'local-storage'): - continue - for pbd in sr_rec['PBDs']: - pbd_rec = session.xenapi.PBD.get_record(pbd) - if pbd_rec['host'] == host: - return sr - return None def move_vhds_into_sr(session, args): """Moves the VHDs from their copied location to the SR""" @@ -75,13 +42,13 @@ def move_vhds_into_sr(session, args): new_base_copy_uuid = params['new_base_copy_uuid'] new_cow_uuid = params['new_cow_uuid'] - sr_path = get_sr_path(session) + sr_path = params['sr_path'] sr_temp_path = "%s/images/" % sr_path - # Discover the copied VHDs locally, and then set up paths to copy + # Discover the copied VHDs locally, and then set up paths to copy # them to under the SR - source_image_path = "%s/instance%d" % (IMAGE_PATH, instance_id) - source_base_copy_path = "%s/%s.vhd" % (source_image_path, + source_image_path = "%s/instance%d" % ('/images/', instance_id) + source_base_copy_path = "%s/%s.vhd" % (source_image_path, old_base_copy_uuid) source_cow_path = "%s/%s.vhd" % (source_image_path, old_cow_uuid) @@ -102,11 +69,10 @@ def move_vhds_into_sr(session, args): os.rmdir(source_image_path) # Link the COW to the base copy - logging.debug('Attaching COW to the base copy %s -> %s' % + logging.debug('Attaching COW to the base copy %s -> %s' % (new_cow_path, new_base_copy_path)) - subprocess.call([VHD_UTIL, 'modify', '-n', new_cow_path, '-p', - new_base_copy_path]) - + subprocess.call(shlex.split('/usr/sbin/vhd-util modify -n %s -p %s' % + (new_cow_path, new_base_copy_path))) logging.debug('Moving VHDs into SR %s' % sr_path) shutil.move("%s/%s.vhd" % (temp_vhd_path, new_base_copy_uuid), sr_path) shutil.move("%s/%s.vhd" % (temp_vhd_path, new_cow_uuid), sr_path) @@ -122,19 +88,19 @@ def transfer_vhd(session, args): instance_id = params['instance_id'] host = params['host'] vdi_uuid = params['vdi_uuid'] - sr_path = get_sr_path(session) + sr_path = params['sr_path'] vhd_path = "%s.vhd" % vdi_uuid source_path = "%s/%s" % (sr_path, vhd_path) - dest_path = '%s:%sinstance%d/' % (host, IMAGE_PATH, instance_id) + dest_path = '%s:%sinstance%d/' % (host, '/images/', instance_id) - logging.debug("Preparing to transmit %s to %s" % (source_path, + logging.debug("Preparing to transmit %s to %s" % (source_path, dest_path)) ssh_cmd = 'ssh -o StrictHostKeyChecking=no' - rsync_args = ['nohup', RSYNC, '-av', '--progress', '-e', ssh_cmd, - source_path, dest_path] + rsync_args = shlex.split('nohup /usr/bin/rsync -av --progress -e %s %s %s' + % (ssh_cmd, source_path, dest_path)) logging.debug('rsync %s' % (' '.join(rsync_args, ))) @@ -148,4 +114,4 @@ def transfer_vhd(session, args): if __name__ == '__main__': XenAPIPlugin.dispatch({'transfer_vhd': transfer_vhd, - 'move_vhds_into_sr':move_vhds_into_sr, }) + 'move_vhds_into_sr': move_vhds_into_sr, }) -- cgit From 7f3dbdab80a4b36a75c860fe1748dfbd03228f2a Mon Sep 17 00:00:00 2001 From: Ken Pepple Date: Mon, 28 Feb 2011 11:29:52 -0800 Subject: moving nova-manage integration tests to smoke tests --- nova/tests/test_nova_manage.py | 130 ----------------------------------------- 1 file changed, 130 deletions(-) delete mode 100644 nova/tests/test_nova_manage.py diff --git a/nova/tests/test_nova_manage.py b/nova/tests/test_nova_manage.py deleted file mode 100644 index 03aea53c9..000000000 --- a/nova/tests/test_nova_manage.py +++ /dev/null @@ -1,130 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# 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. -""" -Tests For Nova-Manage -""" - -import time -import os -import subprocess - -from nova import test -from nova.db.sqlalchemy.session import get_session -from nova.db.sqlalchemy import models - - -class NovaManageTestCase(test.TestCase): - """Test case for nova-manage""" - def setUp(self): - super(NovaManageTestCase, self).setUp() - session = get_session() - max_flavorid = session.query(models.InstanceTypes).\ - order_by("flavorid desc").first() - self.flavorid = str(max_flavorid["flavorid"] + 1) - self.name = str(int(time.time())) - self.fnull = open(os.devnull, 'w') - - def teardown(self): - self.fnull.close() - - def test_create_and_delete_instance_types(self): - myname = self.name + "create_and_delete" - retcode = subprocess.call([ - "bin/nova-manage", - "instance_type", - "create", - self.name, - "256", - "1", - "120", - self.flavorid, - "2", - "10", - "10"], - stdout=self.fnull) - self.assertEqual(0, retcode) - retcode = subprocess.call(["bin/nova-manage", "instance_type", - "delete", self.name], stdout=self.fnull) - self.assertEqual(0, retcode) - retcode = subprocess.call(["bin/nova-manage", "instance_type", - "delete", self.name, "--purge"], - stdout=self.fnull) - self.assertEqual(0, retcode) - - def test_list_instance_types_or_flavors(self): - for c in ["instance_type", "flavor"]: - retcode = subprocess.call(["bin/nova-manage", c, \ - "list"], stdout=self.fnull) - self.assertEqual(0, retcode) - - def test_list_specific_instance_type(self): - retcode = subprocess.call(["bin/nova-manage", "instance_type", "list", - "m1.medium"], stdout=self.fnull) - self.assertEqual(0, retcode) - - def test_should_error_on_bad_create_args(self): - # shouldn't be able to create instance type with 0 vcpus - retcode = subprocess.call(["bin/nova-manage", "instance_type", - "create", self.name + "bad_args", - "256", "0", "120", self.flavorid], - stdout=self.fnull) - self.assertEqual(1, retcode) - - def test_should_fail_on_duplicate_flavorid(self): - # flavorid 1 is set in migration seed data - retcode = subprocess.call(["bin/nova-manage", "instance_type",\ - "create", self.name + "dupflavor", "256", - "1", "120", "1"], stdout=self.fnull) - self.assertEqual(3, retcode) - - def test_should_fail_on_duplicate_name(self): - duplicate_name = self.name + "dup_name" - retcode = subprocess.call([ - "bin/nova-manage", - "instance_type", - "create", - duplicate_name, - "256", - "1", - "120", - self.flavorid, - "2", - "10", - "10"], - stdout=self.fnull) - self.assertEqual(0, retcode) - duplicate_retcode = subprocess.call([ - "bin/nova-manage", - "instance_type", - "create", - duplicate_name, - "512", - "1", - "240", - str(int(self.flavorid) + 1), - "2", - "10", - "10"], - stdout=self.fnull) - self.assertEqual(3, duplicate_retcode) - delete_retcode = subprocess.call(["bin/nova-manage", "instance_type", - "delete", duplicate_name, "--purge"], - stdout=self.fnull) - self.assertEqual(0, delete_retcode) - - def test_instance_type_delete_should_fail_without_valid_name(self): - retcode = subprocess.call(["bin/nova-manage", "instance_type", - "delete", "doesntexist"], - stdout=self.fnull) - self.assertEqual(1, retcode) -- cgit From 05a96b320cf1d6b911b0edb11df0ed408a894e77 Mon Sep 17 00:00:00 2001 From: Brian Lamar Date: Mon, 28 Feb 2011 14:49:03 -0500 Subject: Edited `nova.api.openstack.common:limited` method to raise an HTTPBadRequest exception if a negative limit or offset is given. I'm not confident that this is the correct approach, because I guess this method could be called out of an API/WSGI context, but the method *is* located in the OpenStack API module and is currently only used in WSGI-capable methods, so we should be safe. --- nova/api/openstack/common.py | 8 +++++++- nova/tests/api/openstack/test_common.py | 21 +++++++++++++++++++++ 2 files changed, 28 insertions(+), 1 deletion(-) diff --git a/nova/api/openstack/common.py b/nova/api/openstack/common.py index 1dc3767e2..9f85c5c8a 100644 --- a/nova/api/openstack/common.py +++ b/nova/api/openstack/common.py @@ -15,6 +15,8 @@ # License for the specific language governing permissions and limitations # under the License. +import webob.exc + from nova import exception @@ -27,7 +29,8 @@ def limited(items, request, max_limit=1000): GET variables. 'offset' is where to start in the list, and 'limit' is the maximum number of items to return. If 'limit' is not specified, 0, or > max_limit, we default - to max_limit. + to max_limit. Negative values for either offset or limit + will cause exc.HTTPBadRequest() exceptions to be raised. @kwarg max_limit: The maximum number of items to return from 'items' """ try: @@ -40,6 +43,9 @@ def limited(items, request, max_limit=1000): except ValueError: limit = max_limit + if offset < 0 or limit < 0: + raise webob.exc.HTTPBadRequest() + limit = min(max_limit, limit or max_limit) range_end = offset + limit return items[offset:range_end] diff --git a/nova/tests/api/openstack/test_common.py b/nova/tests/api/openstack/test_common.py index 59d850157..92023362c 100644 --- a/nova/tests/api/openstack/test_common.py +++ b/nova/tests/api/openstack/test_common.py @@ -19,6 +19,7 @@ Test suites for 'common' code used throughout the OpenStack HTTP API. """ +import webob.exc from webob import Request @@ -160,3 +161,23 @@ class LimiterTest(test.TestCase): self.assertEqual(limited(items, req, max_limit=2000), items[3:]) req = Request.blank('/?offset=3000&limit=10') self.assertEqual(limited(items, req, max_limit=2000), []) + + def test_limiter_negative_limit(self): + """ + Test a negative limit. + """ + def _limit_large(): + limited(self.large, req, max_limit=2000) + + req = Request.blank('/?limit=-3000') + self.assertRaises(webob.exc.HTTPBadRequest, _limit_large) + + def test_limiter_negative_offset(self): + """ + Test a negative offset. + """ + def _limit_large(): + limited(self.large, req, max_limit=2000) + + req = Request.blank('/?offset=-30') + self.assertRaises(webob.exc.HTTPBadRequest, _limit_large) -- cgit From 167de65b41aa7188f01ace2359074abc8029ada2 Mon Sep 17 00:00:00 2001 From: Ken Pepple Date: Mon, 28 Feb 2011 11:49:17 -0800 Subject: moved nova-manage flavors docs --- doc/source/adminguide/managing.instance.types.rst | 83 ----------------------- doc/source/runnova/managing.instance.types.rst | 83 +++++++++++++++++++++++ 2 files changed, 83 insertions(+), 83 deletions(-) delete mode 100644 doc/source/adminguide/managing.instance.types.rst create mode 100644 doc/source/runnova/managing.instance.types.rst diff --git a/doc/source/adminguide/managing.instance.types.rst b/doc/source/adminguide/managing.instance.types.rst deleted file mode 100644 index 30a47e47d..000000000 --- a/doc/source/adminguide/managing.instance.types.rst +++ /dev/null @@ -1,83 +0,0 @@ - - Copyright 2010-2011 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. - -Managing Instance Types and Flavors -=================================== - -What are Instance Types or Flavors ? ------------------------------------- - -Instance types are the container descriptions (meta-data) about instances. In layman terms, this is the size of the instance (vCPUs, RAM, etc.) that you will be launching. In the EC2 API, these are called by names such as "m1.large" or "m1.tiny", while the OpenStack API terms these "flavors" with names like "" - -Flavors are simply the name for instance types used in the OpenStack API. In nova, these are equivalent terms, so when you create an instance type you are also creating a flavor. For the rest of this document, I will refer to these as instance types. - -In the current (Cactus) version of nova, instance types can only be created by the nova administrator through the nova-manage command. Future versions of nova (in concert with the OpenStack API or EC2 API), may expose this functionality directly to users. - -Basic Management ----------------- - -Instance types / flavor are managed through the nova-manage binary with -the "instance_type" command and an appropriate subcommand. Note that you can also use -the "flavor" command as a synonym for "instance_types". - -To see all currently active instance types, use the list subcommand:: - - # nova-manage instance_type list - m1.medium: Memory: 4096MB, VCPUS: 2, Storage: 40GB, FlavorID: 3, Swap: 0GB, RXTX Quota: 0GB, RXTX Cap: 0MB - m1.large: Memory: 8192MB, VCPUS: 4, Storage: 80GB, FlavorID: 4, Swap: 0GB, RXTX Quota: 0GB, RXTX Cap: 0MB - m1.tiny: Memory: 512MB, VCPUS: 1, Storage: 0GB, FlavorID: 1, Swap: 0GB, RXTX Quota: 0GB, RXTX Cap: 0MB - m1.xlarge: Memory: 16384MB, VCPUS: 8, Storage: 160GB, FlavorID: 5, Swap: 0GB, RXTX Quota: 0GB, RXTX Cap: 0MB - m1.small: Memory: 2048MB, VCPUS: 1, Storage: 20GB, FlavorID: 2, Swap: 0GB, RXTX Quota: 0GB, RXTX Cap: 0MB - -By default, the list subcommand only shows active instance types. To see all instance types -(even those deleted), add the argument 1 after the list subcommand like so:: - - # nova-manage instance_type list 1 - m1.medium: Memory: 4096MB, VCPUS: 2, Storage: 40GB, FlavorID: 3, Swap: 0GB, RXTX Quota: 0GB, RXTX Cap: 0MB - m1.large: Memory: 8192MB, VCPUS: 4, Storage: 80GB, FlavorID: 4, Swap: 0GB, RXTX Quota: 0GB, RXTX Cap: 0MB - m1.tiny: Memory: 512MB, VCPUS: 1, Storage: 0GB, FlavorID: 1, Swap: 0GB, RXTX Quota: 0GB, RXTX Cap: 0MB - m1.xlarge: Memory: 16384MB, VCPUS: 8, Storage: 160GB, FlavorID: 5, Swap: 0GB, RXTX Quota: 0GB, RXTX Cap: 0MB - m1.small: Memory: 2048MB, VCPUS: 1, Storage: 20GB, FlavorID: 2, Swap: 0GB, RXTX Quota: 0GB, RXTX Cap: 0MB - m1.deleted: Memory: 2048MB, VCPUS: 1, Storage: 20GB, FlavorID: 2, Swap: 0GB, RXTX Quota: 0GB, RXTX Cap: 0MB - -To create an instance type, use the "create" subcommand with the following positional arguments: - * memory (expressed in megabytes) - * vcpu(s) (integer) - * local storage (expressed in gigabytes) - * flavorid (unique integer) - * swap space (expressed in megabytes, defaults to zero, optional) - * RXTX quotas (expressed in gigabytes, defaults to zero, optional) - * RXTX cap (expressed in gigabytes, defaults to zero, optional) - -The following example creates an instance type named "m1.xxlarge":: - - # nova-manage instance_type create m1.xxlarge 32768 16 320 0 0 0 - m1.xxlarge created - -To delete an instance type, use the "delete" subcommand and specify the name:: - - # nova-manage instance_type delete m1.xxlarge - m1.xxlarge deleted - -Please note that the "delete" command only marks the instance type as -inactive in the database; it does not actually remove the instance type. This is done -to preserve the instance type definition for long running instances (which may not -terminate for months or years). If you are sure that you want to delete this instance -type from the database, pass the "--purge" flag after the name:: - - # nova-manage instance_type delete m1.xxlarge --purge - m1.xxlarge deleted diff --git a/doc/source/runnova/managing.instance.types.rst b/doc/source/runnova/managing.instance.types.rst new file mode 100644 index 000000000..30a47e47d --- /dev/null +++ b/doc/source/runnova/managing.instance.types.rst @@ -0,0 +1,83 @@ + + Copyright 2010-2011 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. + +Managing Instance Types and Flavors +=================================== + +What are Instance Types or Flavors ? +------------------------------------ + +Instance types are the container descriptions (meta-data) about instances. In layman terms, this is the size of the instance (vCPUs, RAM, etc.) that you will be launching. In the EC2 API, these are called by names such as "m1.large" or "m1.tiny", while the OpenStack API terms these "flavors" with names like "" + +Flavors are simply the name for instance types used in the OpenStack API. In nova, these are equivalent terms, so when you create an instance type you are also creating a flavor. For the rest of this document, I will refer to these as instance types. + +In the current (Cactus) version of nova, instance types can only be created by the nova administrator through the nova-manage command. Future versions of nova (in concert with the OpenStack API or EC2 API), may expose this functionality directly to users. + +Basic Management +---------------- + +Instance types / flavor are managed through the nova-manage binary with +the "instance_type" command and an appropriate subcommand. Note that you can also use +the "flavor" command as a synonym for "instance_types". + +To see all currently active instance types, use the list subcommand:: + + # nova-manage instance_type list + m1.medium: Memory: 4096MB, VCPUS: 2, Storage: 40GB, FlavorID: 3, Swap: 0GB, RXTX Quota: 0GB, RXTX Cap: 0MB + m1.large: Memory: 8192MB, VCPUS: 4, Storage: 80GB, FlavorID: 4, Swap: 0GB, RXTX Quota: 0GB, RXTX Cap: 0MB + m1.tiny: Memory: 512MB, VCPUS: 1, Storage: 0GB, FlavorID: 1, Swap: 0GB, RXTX Quota: 0GB, RXTX Cap: 0MB + m1.xlarge: Memory: 16384MB, VCPUS: 8, Storage: 160GB, FlavorID: 5, Swap: 0GB, RXTX Quota: 0GB, RXTX Cap: 0MB + m1.small: Memory: 2048MB, VCPUS: 1, Storage: 20GB, FlavorID: 2, Swap: 0GB, RXTX Quota: 0GB, RXTX Cap: 0MB + +By default, the list subcommand only shows active instance types. To see all instance types +(even those deleted), add the argument 1 after the list subcommand like so:: + + # nova-manage instance_type list 1 + m1.medium: Memory: 4096MB, VCPUS: 2, Storage: 40GB, FlavorID: 3, Swap: 0GB, RXTX Quota: 0GB, RXTX Cap: 0MB + m1.large: Memory: 8192MB, VCPUS: 4, Storage: 80GB, FlavorID: 4, Swap: 0GB, RXTX Quota: 0GB, RXTX Cap: 0MB + m1.tiny: Memory: 512MB, VCPUS: 1, Storage: 0GB, FlavorID: 1, Swap: 0GB, RXTX Quota: 0GB, RXTX Cap: 0MB + m1.xlarge: Memory: 16384MB, VCPUS: 8, Storage: 160GB, FlavorID: 5, Swap: 0GB, RXTX Quota: 0GB, RXTX Cap: 0MB + m1.small: Memory: 2048MB, VCPUS: 1, Storage: 20GB, FlavorID: 2, Swap: 0GB, RXTX Quota: 0GB, RXTX Cap: 0MB + m1.deleted: Memory: 2048MB, VCPUS: 1, Storage: 20GB, FlavorID: 2, Swap: 0GB, RXTX Quota: 0GB, RXTX Cap: 0MB + +To create an instance type, use the "create" subcommand with the following positional arguments: + * memory (expressed in megabytes) + * vcpu(s) (integer) + * local storage (expressed in gigabytes) + * flavorid (unique integer) + * swap space (expressed in megabytes, defaults to zero, optional) + * RXTX quotas (expressed in gigabytes, defaults to zero, optional) + * RXTX cap (expressed in gigabytes, defaults to zero, optional) + +The following example creates an instance type named "m1.xxlarge":: + + # nova-manage instance_type create m1.xxlarge 32768 16 320 0 0 0 + m1.xxlarge created + +To delete an instance type, use the "delete" subcommand and specify the name:: + + # nova-manage instance_type delete m1.xxlarge + m1.xxlarge deleted + +Please note that the "delete" command only marks the instance type as +inactive in the database; it does not actually remove the instance type. This is done +to preserve the instance type definition for long running instances (which may not +terminate for months or years). If you are sure that you want to delete this instance +type from the database, pass the "--purge" flag after the name:: + + # nova-manage instance_type delete m1.xxlarge --purge + m1.xxlarge deleted -- cgit From 7ad5fe27144e592df7e794a0748301d41603377e Mon Sep 17 00:00:00 2001 From: Ken Pepple Date: Mon, 28 Feb 2011 13:16:07 -0800 Subject: refactored nova-manage list (-all, ) and fixed docs --- bin/nova-manage | 37 +++++++++++++------------- doc/source/man/novamanage.rst | 35 ++++++++++++++++++++++++ doc/source/runnova/managing.instance.types.rst | 14 ++++++++-- nova/db/sqlalchemy/api.py | 18 +++++++------ 4 files changed, 75 insertions(+), 29 deletions(-) diff --git a/bin/nova-manage b/bin/nova-manage index b977eb94d..0b71da41e 100755 --- a/bin/nova-manage +++ b/bin/nova-manage @@ -666,8 +666,9 @@ class InstanceTypeCommands(object): """Class for managing instance types / flavors.""" def _print_instance_types(self, n, val): + deleted = ('', ', inactive')[val["deleted"] == 1] print ("%s: Memory: %sMB, VCPUS: %s, Storage: %sGB, FlavorID: %s, " - "Swap: %sGB, RXTX Quota: %sGB, RXTX Cap: %sMB") % ( + "Swap: %sGB, RXTX Quota: %sGB, RXTX Cap: %sMB%s") % ( n, val["memory_mb"], val["vcpus"], @@ -675,7 +676,8 @@ class InstanceTypeCommands(object): val["flavorid"], val["swap"], val["rxtx_quota"], - val["rxtx_cap"]) + val["rxtx_cap"], + deleted) def create( self, @@ -688,8 +690,8 @@ class InstanceTypeCommands(object): rxtx_quota=0, rxtx_cap=0): """Creates instance types / flavors - arguments: name memory vcpus local_gb flavorid swap rxtx_quota - rxtx_cap + arguments: name memory vcpus local_gb flavorid [swap] [rxtx_quota] + [rxtx_cap] """ try: instance_types.create( @@ -736,25 +738,22 @@ class InstanceTypeCommands(object): print "%s %s" % (name, verb) def list(self, name=None): - """Lists all or specific instance types / flavors + """Lists all active or specific instance types / flavors arguments: [name]""" - if name == None: - try: + try: + if name == None: inst_types = instance_types.get_all_types() - except exception.NotFound, e: - print e - sys.exit(1) + elif name == "--all": + inst_types = instance_types.get_all_types(1) else: - for k, v in inst_types.iteritems(): - self._print_instance_types(k, v) - else: - try: inst_types = instance_types.get_instance_type(name) - except exception.NotFound, e: - print e - sys.exit(1) - else: - self._print_instance_types(name, inst_types) + except exception.DBError, e: + _db_error(e) + if isinstance(inst_types.values()[0], dict): + for k, v in inst_types.iteritems(): + self._print_instance_types(k, v) + else: + self._print_instance_types(name, inst_types) CATEGORIES = [ diff --git a/doc/source/man/novamanage.rst b/doc/source/man/novamanage.rst index bb9d7a7fe..94e93129a 100644 --- a/doc/source/man/novamanage.rst +++ b/doc/source/man/novamanage.rst @@ -179,6 +179,41 @@ Nova Floating IPs Displays a list of all floating IP addresses. +Nova Flavor +~~~~~~~~~~~ + +``nova-manage flavor list`` + + Outputs a list of all active flavors to the screen. + +``nova-manage flavor list --all`` + + Outputs a list of all flavors (active and inactive) to the screen. + +``nova-manage flavor create `` + + creates a flavor with the following positional arguments: + * memory (expressed in megabytes) + * vcpu(s) (integer) + * local storage (expressed in gigabytes) + * flavorid (unique integer) + * swap space (expressed in megabytes, defaults to zero, optional) + * RXTX quotas (expressed in gigabytes, defaults to zero, optional) + * RXTX cap (expressed in gigabytes, defaults to zero, optional) + +``nova-manage flavor delete `` + + Delete the flavor with the name . This marks the flavor as inactive and cannot be launched. However, the record stays in the database for archival and billing purposes. + +``nova-manage flavor delete --purge`` + + Purges the flavor with the name . This removes this flavor from the database. + + +Nova Instance_type +~~~~~~~~~~~~~~~~~~ + +Nova instance_type is a alias for FILES ======== diff --git a/doc/source/runnova/managing.instance.types.rst b/doc/source/runnova/managing.instance.types.rst index 30a47e47d..656268f8e 100644 --- a/doc/source/runnova/managing.instance.types.rst +++ b/doc/source/runnova/managing.instance.types.rst @@ -44,9 +44,9 @@ To see all currently active instance types, use the list subcommand:: m1.small: Memory: 2048MB, VCPUS: 1, Storage: 20GB, FlavorID: 2, Swap: 0GB, RXTX Quota: 0GB, RXTX Cap: 0MB By default, the list subcommand only shows active instance types. To see all instance types -(even those deleted), add the argument 1 after the list subcommand like so:: +(even those deleted), use the listall subcommand:: - # nova-manage instance_type list 1 + # nova-manage instance_type listall m1.medium: Memory: 4096MB, VCPUS: 2, Storage: 40GB, FlavorID: 3, Swap: 0GB, RXTX Quota: 0GB, RXTX Cap: 0MB m1.large: Memory: 8192MB, VCPUS: 4, Storage: 80GB, FlavorID: 4, Swap: 0GB, RXTX Quota: 0GB, RXTX Cap: 0MB m1.tiny: Memory: 512MB, VCPUS: 1, Storage: 0GB, FlavorID: 1, Swap: 0GB, RXTX Quota: 0GB, RXTX Cap: 0MB @@ -81,3 +81,13 @@ type from the database, pass the "--purge" flag after the name:: # nova-manage instance_type delete m1.xxlarge --purge m1.xxlarge deleted + +To see all instance types (inactive + active), use the list subcommand with the "--all" flag:: + + # nova-manage instance_type list --all + m1.medium: Memory: 4096MB, VCPUS: 2, Storage: 40GB, FlavorID: 3, Swap: 0GB, RXTX Quota: 0GB, RXTX Cap: 0MB + m1.large: Memory: 8192MB, VCPUS: 4, Storage: 80GB, FlavorID: 4, Swap: 0GB, RXTX Quota: 0GB, RXTX Cap: 0MB + m1.tiny: Memory: 512MB, VCPUS: 1, Storage: 0GB, FlavorID: 1, Swap: 0GB, RXTX Quota: 0GB, RXTX Cap: 0MB + m1.xlarge: Memory: 16384MB, VCPUS: 8, Storage: 160GB, FlavorID: 5, Swap: 0GB, RXTX Quota: 0GB, RXTX Cap: 0MB + m1.small: Memory: 2048MB, VCPUS: 1, Storage: 20GB, FlavorID: 2, Swap: 0GB, RXTX Quota: 0GB, RXTX Cap: 0MB + diff --git a/nova/db/sqlalchemy/api.py b/nova/db/sqlalchemy/api.py index d47b11891..f4cd16d9a 100644 --- a/nova/db/sqlalchemy/api.py +++ b/nova/db/sqlalchemy/api.py @@ -2090,16 +2090,18 @@ def instance_type_create(context, values): @require_context def instance_type_get_all(context, inactive=0): """ - Returns a dict describing all non-deleted instance_types with name as key: - {'m1.tiny': dict(memory_mb=512, vcpus=1, local_gb=0, flavorid=1), - 'm1.small': dict(memory_mb=2048, vcpus=1, local_gb=20, flavorid=2), - 'm1.medium': dict(memory_mb=4096, vcpus=2, local_gb=40, flavorid=3)} + Returns a dict describing all instance_types with name as key. """ session = get_session() - inst_types = session.query(models.InstanceTypes).\ - filter_by(deleted=inactive).\ - order_by("name").\ - all() + if inactive: + inst_types = session.query(models.InstanceTypes).\ + order_by("name").\ + all() + else: + inst_types = session.query(models.InstanceTypes).\ + filter_by(deleted=inactive).\ + order_by("name").\ + all() if inst_types: inst_dict = {} for i in inst_types: -- cgit From 2e12ee1c98241ac38b59e93fb7b4b05b66ccadc9 Mon Sep 17 00:00:00 2001 From: Ken Pepple Date: Mon, 28 Feb 2011 15:05:50 -0800 Subject: fixed pep8 --- plugins/xenserver/xenapi/etc/xapi.d/plugins/glance | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/plugins/xenserver/xenapi/etc/xapi.d/plugins/glance b/plugins/xenserver/xenapi/etc/xapi.d/plugins/glance index 7531af4ec..aa12d432a 100644 --- a/plugins/xenserver/xenapi/etc/xapi.d/plugins/glance +++ b/plugins/xenserver/xenapi/etc/xapi.d/plugins/glance @@ -207,8 +207,7 @@ def _upload_tarball(staging_path, image_id, glance_host, glance_port): 'transfer-encoding': 'chunked', 'x-image-meta-is_public': 'True', 'x-image-meta-status': 'queued', - 'x-image-meta-type': 'vhd' - } + 'x-image-meta-type': 'vhd'} for header, value in headers.iteritems(): conn.putheader(header, value) conn.endheaders() -- cgit From aafd83233a675ccf5f3c4e737d966652e45a0ecb Mon Sep 17 00:00:00 2001 From: Ken Pepple Date: Mon, 28 Feb 2011 16:28:46 -0800 Subject: replaced ugly INSTANCE_TYPE constant with (slightly less ugly) stubs --- nova/test.py | 8 -------- nova/tests/db/fakes.py | 20 ++++++++++++++++++-- nova/tests/test_quota.py | 17 ++++++++++++++--- nova/tests/test_xenapi.py | 2 +- 4 files changed, 33 insertions(+), 14 deletions(-) diff --git a/nova/test.py b/nova/test.py index 0329383b8..d8a47464f 100644 --- a/nova/test.py +++ b/nova/test.py @@ -48,14 +48,6 @@ flags.DEFINE_bool('fake_tests', True, 'should we use everything for testing') -INSTANCE_TYPES = { - 'm1.tiny': dict(memory_mb=512, vcpus=1, local_gb=0, flavorid=1), - 'm1.small': dict(memory_mb=2048, vcpus=1, local_gb=20, flavorid=2), - 'm1.medium': dict(memory_mb=4096, vcpus=2, local_gb=40, flavorid=3), - 'm1.large': dict(memory_mb=8192, vcpus=4, local_gb=80, flavorid=4), - 'm1.xlarge': dict(memory_mb=16384, vcpus=8, local_gb=160, flavorid=5)} - - def skip_if_fake(func): """Decorator that skips a test if running in fake mode""" def _skipper(*args, **kw): diff --git a/nova/tests/db/fakes.py b/nova/tests/db/fakes.py index d9a5032ee..d760dc456 100644 --- a/nova/tests/db/fakes.py +++ b/nova/tests/db/fakes.py @@ -22,12 +22,20 @@ import time from nova import db from nova import test from nova import utils -from nova.compute import instance_types def stub_out_db_instance_api(stubs): """ Stubs out the db API for creating Instances """ + INSTANCE_TYPES = { + 'm1.tiny': dict(memory_mb=512, vcpus=1, local_gb=0, flavorid=1), + 'm1.small': dict(memory_mb=2048, vcpus=1, local_gb=20, flavorid=2), + 'm1.medium': + dict(memory_mb=4096, vcpus=2, local_gb=40, flavorid=3), + 'm1.large': dict(memory_mb=8192, vcpus=4, local_gb=80, flavorid=4), + 'm1.xlarge': + dict(memory_mb=16384, vcpus=8, local_gb=160, flavorid=5)} + class FakeModel(object): """ Stubs out for model """ def __init__(self, values): @@ -42,10 +50,16 @@ def stub_out_db_instance_api(stubs): else: raise NotImplementedError() + def fake_instance_type_get_all(context, inactive=0): + return INSTANCE_TYPES + + def fake_instance_type_get_by_name(context, name): + return INSTANCE_TYPES[name] + def fake_instance_create(values): """ Stubs out the db.instance_create method """ - type_data = test.INSTANCE_TYPES[values['instance_type']] + type_data = INSTANCE_TYPES[values['instance_type']] base_options = { 'name': values['name'], @@ -74,3 +88,5 @@ def stub_out_db_instance_api(stubs): stubs.Set(db, 'instance_create', fake_instance_create) stubs.Set(db, 'network_get_by_instance', fake_network_get_by_instance) + stubs.Set(db, 'instance_type_get_all', fake_instance_type_get_all) + stubs.Set(db, 'instance_type_get_by_name', fake_instance_type_get_by_name) diff --git a/nova/tests/test_quota.py b/nova/tests/test_quota.py index faafe2d31..4ecb36b54 100644 --- a/nova/tests/test_quota.py +++ b/nova/tests/test_quota.py @@ -74,19 +74,30 @@ class QuotaTestCase(test.TestCase): vol['size'] = size return db.volume_create(self.context, vol)['id'] + def _get_instance_type(self, name): + instance_types = { + 'm1.tiny': dict(memory_mb=512, vcpus=1, local_gb=0, flavorid=1), + 'm1.small': dict(memory_mb=2048, vcpus=1, local_gb=20, flavorid=2), + 'm1.medium': + dict(memory_mb=4096, vcpus=2, local_gb=40, flavorid=3), + 'm1.large': dict(memory_mb=8192, vcpus=4, local_gb=80, flavorid=4), + 'm1.xlarge': + dict(memory_mb=16384, vcpus=8, local_gb=160, flavorid=5)} + return instance_types[name] + def test_quota_overrides(self): """Make sure overriding a projects quotas works""" num_instances = quota.allowed_instances(self.context, 100, - test.INSTANCE_TYPES['m1.small']) + self._get_instance_type('m1.small')) self.assertEqual(num_instances, 2) db.quota_create(self.context, {'project_id': self.project.id, 'instances': 10}) num_instances = quota.allowed_instances(self.context, 100, - test.INSTANCE_TYPES['m1.small']) + self._get_instance_type('m1.small')) self.assertEqual(num_instances, 4) db.quota_update(self.context, self.project.id, {'cores': 100}) num_instances = quota.allowed_instances(self.context, 100, - test.INSTANCE_TYPES['m1.small']) + self._get_instance_type('m1.small')) self.assertEqual(num_instances, 10) # metadata_items diff --git a/nova/tests/test_xenapi.py b/nova/tests/test_xenapi.py index 27131c45e..106c0bd6f 100644 --- a/nova/tests/test_xenapi.py +++ b/nova/tests/test_xenapi.py @@ -233,7 +233,7 @@ class XenAPIVMTestCase(test.TestCase): vm = vms[0] # Check that m1.large above turned into the right thing. - instance_type = test.INSTANCE_TYPES['m1.large'] + instance_type = db.instance_type_get_by_name(conn, 'm1.large') mem_kib = long(instance_type['memory_mb']) << 10 mem_bytes = str(mem_kib << 10) vcpus = instance_type['vcpus'] -- cgit From 69779dcdc5584fafa95974f263cef14912a12ed7 Mon Sep 17 00:00:00 2001 From: Ken Pepple Date: Mon, 28 Feb 2011 17:06:35 -0800 Subject: refactored adminclient --- nova/api/ec2/admin.py | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/nova/api/ec2/admin.py b/nova/api/ec2/admin.py index d867e5da9..51a06bb26 100644 --- a/nova/api/ec2/admin.py +++ b/nova/api/ec2/admin.py @@ -29,7 +29,6 @@ from nova import flags from nova import log as logging from nova import utils from nova.auth import manager -from nova.compute import instance_types FLAGS = flags.FLAGS @@ -115,21 +114,10 @@ class AdminController(object): def __str__(self): return 'AdminController' - # FIXME(kpepple) this is untested code path. def describe_instance_types(self, _context, **_kwargs): """Returns all active instance types data (vcpus, memory, etc.)""" - # return {'instanceTypeSet': [instance_dict(n, v) for n, v in - # instance_types.INSTANCE_TYPES.iteritems()]} - # return {'instanceTypeSet': - # [for i in db.instance_type_get_all(): instance_dict(i)]} return {'instanceTypeSet': [db.instance_type_get_all(_context)]} - # FIXME(kpepple) this is untested code path. - def describe_instance_type(self, _context, name, **_kwargs): - """Returns a specific active instance types data""" - return {'instanceTypeSet': - [instance_dict(db.instance_type_get_by_name(name))]} - def describe_user(self, _context, name, **_kwargs): """Returns user data, including access and secret keys.""" return user_dict(manager.AuthManager().get_user(name)) -- cgit From 160d5751486ef7658089868eaeba23b87b695925 Mon Sep 17 00:00:00 2001 From: Ken Pepple Date: Mon, 28 Feb 2011 18:32:55 -0800 Subject: updated docs --- doc/source/api/autoindex.rst | 6 ++++++ doc/source/man/novamanage.rst | 5 +++-- doc/source/nova.concepts.rst | 5 +++++ doc/source/runnova/managing.instance.types.rst | 29 ++++++++++---------------- 4 files changed, 25 insertions(+), 20 deletions(-) diff --git a/doc/source/api/autoindex.rst b/doc/source/api/autoindex.rst index 41fc1f4a9..329a465db 100644 --- a/doc/source/api/autoindex.rst +++ b/doc/source/api/autoindex.rst @@ -43,6 +43,9 @@ nova..db.sqlalchemy.migrate_repo.versions.002_bexar.rst nova..db.sqlalchemy.migrate_repo.versions.003_add_label_to_networks.rst nova..db.sqlalchemy.migrate_repo.versions.004_add_zone_tables.rst + nova..db.sqlalchemy.migrate_repo.versions.005_add_instance_metadata.rst + nova..db.sqlalchemy.migrate_repo.versions.006_add_provider_data_to_volumes.rst + nova..db.sqlalchemy.migrate_repo.versions.007_add_instance_types.rst nova..db.sqlalchemy.migration.rst nova..db.sqlalchemy.models.rst nova..db.sqlalchemy.session.rst @@ -101,6 +104,7 @@ nova..tests.test_console.rst nova..tests.test_direct.rst nova..tests.test_flags.rst + nova..tests.test_instance_types.rst nova..tests.test_localization.rst nova..tests.test_log.rst nova..tests.test_middleware.rst @@ -110,7 +114,9 @@ nova..tests.test_rpc.rst nova..tests.test_scheduler.rst nova..tests.test_service.rst + nova..tests.test_test.rst nova..tests.test_twistd.rst + nova..tests.test_utils.rst nova..tests.test_virt.rst nova..tests.test_volume.rst nova..tests.test_xenapi.rst diff --git a/doc/source/man/novamanage.rst b/doc/source/man/novamanage.rst index 94e93129a..17ba91bef 100644 --- a/doc/source/man/novamanage.rst +++ b/doc/source/man/novamanage.rst @@ -190,7 +190,7 @@ Nova Flavor Outputs a list of all flavors (active and inactive) to the screen. -``nova-manage flavor create `` +``nova-manage flavor create <(optional) swap> <(optional) RXTX Quota> <(optional) RXTX Cap>`` creates a flavor with the following positional arguments: * memory (expressed in megabytes) @@ -213,7 +213,8 @@ Nova Flavor Nova Instance_type ~~~~~~~~~~~~~~~~~~ -Nova instance_type is a alias for +The instance_type command is provided as an alias for the flavor command. All the same subcommands and arguments from nova-manage flavor can be used. + FILES ======== diff --git a/doc/source/nova.concepts.rst b/doc/source/nova.concepts.rst index e9687dc98..45cc4b879 100644 --- a/doc/source/nova.concepts.rst +++ b/doc/source/nova.concepts.rst @@ -64,6 +64,11 @@ Concept: Instances An 'instance' is a word for a virtual machine that runs inside the cloud. +Concept: Instance Type +---------------------- + +An 'instance type' describes the compute, memory and storage capacity of nova computing instances. In layman terms, this is the size (in terms of vCPUs, RAM, etc.) of the virtual server that you will be launching. + Concept: System Architecture ---------------------------- diff --git a/doc/source/runnova/managing.instance.types.rst b/doc/source/runnova/managing.instance.types.rst index 656268f8e..37bb2e99e 100644 --- a/doc/source/runnova/managing.instance.types.rst +++ b/doc/source/runnova/managing.instance.types.rst @@ -1,4 +1,4 @@ - +.. Copyright 2010-2011 United States Government as represented by the Administrator of the National Aeronautics and Space Administration. All Rights Reserved. @@ -21,9 +21,13 @@ Managing Instance Types and Flavors What are Instance Types or Flavors ? ------------------------------------ -Instance types are the container descriptions (meta-data) about instances. In layman terms, this is the size of the instance (vCPUs, RAM, etc.) that you will be launching. In the EC2 API, these are called by names such as "m1.large" or "m1.tiny", while the OpenStack API terms these "flavors" with names like "" +Instance types describe the compute, memory and storage capacity of nova computing instances. In layman terms, this is the size (in terms of vCPUs, RAM, etc.) of the virtual server that you will be launching. In the EC2 API, these are called by names such as "m1.large" or "m1.tiny", while the OpenStack API terms these "flavors" with names like "512 MB Server". + +In Nova, "flavor" and "instance type" are equivalent terms. When you create an EC2 instance type, you are also creating a OpenStack API flavor. To reduce repetition, for the rest of this document I will refer to these as instance types. -Flavors are simply the name for instance types used in the OpenStack API. In nova, these are equivalent terms, so when you create an instance type you are also creating a flavor. For the rest of this document, I will refer to these as instance types. +Instance types can be in either the active or inactive state: + * Active instance types are available to be used for launching instances + * Inactive instance types are not available for launching instances In the current (Cactus) version of nova, instance types can only be created by the nova administrator through the nova-manage command. Future versions of nova (in concert with the OpenStack API or EC2 API), may expose this functionality directly to users. @@ -43,16 +47,15 @@ To see all currently active instance types, use the list subcommand:: m1.xlarge: Memory: 16384MB, VCPUS: 8, Storage: 160GB, FlavorID: 5, Swap: 0GB, RXTX Quota: 0GB, RXTX Cap: 0MB m1.small: Memory: 2048MB, VCPUS: 1, Storage: 20GB, FlavorID: 2, Swap: 0GB, RXTX Quota: 0GB, RXTX Cap: 0MB -By default, the list subcommand only shows active instance types. To see all instance types -(even those deleted), use the listall subcommand:: +By default, the list subcommand only shows active instance types. To see all instance types (inactive and active), use the list subcommand with the "--all" flag:: - # nova-manage instance_type listall + # nova-manage instance_type list --all m1.medium: Memory: 4096MB, VCPUS: 2, Storage: 40GB, FlavorID: 3, Swap: 0GB, RXTX Quota: 0GB, RXTX Cap: 0MB m1.large: Memory: 8192MB, VCPUS: 4, Storage: 80GB, FlavorID: 4, Swap: 0GB, RXTX Quota: 0GB, RXTX Cap: 0MB m1.tiny: Memory: 512MB, VCPUS: 1, Storage: 0GB, FlavorID: 1, Swap: 0GB, RXTX Quota: 0GB, RXTX Cap: 0MB m1.xlarge: Memory: 16384MB, VCPUS: 8, Storage: 160GB, FlavorID: 5, Swap: 0GB, RXTX Quota: 0GB, RXTX Cap: 0MB m1.small: Memory: 2048MB, VCPUS: 1, Storage: 20GB, FlavorID: 2, Swap: 0GB, RXTX Quota: 0GB, RXTX Cap: 0MB - m1.deleted: Memory: 2048MB, VCPUS: 1, Storage: 20GB, FlavorID: 2, Swap: 0GB, RXTX Quota: 0GB, RXTX Cap: 0MB + m1.deleted: Memory: 2048MB, VCPUS: 1, Storage: 20GB, FlavorID: 2, Swap: 0GB, RXTX Quota: 0GB, RXTX Cap: 0MB, inactive To create an instance type, use the "create" subcommand with the following positional arguments: * memory (expressed in megabytes) @@ -80,14 +83,4 @@ terminate for months or years). If you are sure that you want to delete this ins type from the database, pass the "--purge" flag after the name:: # nova-manage instance_type delete m1.xxlarge --purge - m1.xxlarge deleted - -To see all instance types (inactive + active), use the list subcommand with the "--all" flag:: - - # nova-manage instance_type list --all - m1.medium: Memory: 4096MB, VCPUS: 2, Storage: 40GB, FlavorID: 3, Swap: 0GB, RXTX Quota: 0GB, RXTX Cap: 0MB - m1.large: Memory: 8192MB, VCPUS: 4, Storage: 80GB, FlavorID: 4, Swap: 0GB, RXTX Quota: 0GB, RXTX Cap: 0MB - m1.tiny: Memory: 512MB, VCPUS: 1, Storage: 0GB, FlavorID: 1, Swap: 0GB, RXTX Quota: 0GB, RXTX Cap: 0MB - m1.xlarge: Memory: 16384MB, VCPUS: 8, Storage: 160GB, FlavorID: 5, Swap: 0GB, RXTX Quota: 0GB, RXTX Cap: 0MB - m1.small: Memory: 2048MB, VCPUS: 1, Storage: 20GB, FlavorID: 2, Swap: 0GB, RXTX Quota: 0GB, RXTX Cap: 0MB - + m1.xxlarge purged -- cgit From 6ddada8ed734dd91502a3f86eed8fb66803d08f3 Mon Sep 17 00:00:00 2001 From: Ken Pepple Date: Mon, 28 Feb 2011 18:33:33 -0800 Subject: updated docs --- doc/.autogenerated | 276 ++--------------------------------------------------- 1 file changed, 6 insertions(+), 270 deletions(-) diff --git a/doc/.autogenerated b/doc/.autogenerated index e4c98ec9b..b11735994 100644 --- a/doc/.autogenerated +++ b/doc/.autogenerated @@ -40,6 +40,9 @@ source/api/nova..db.sqlalchemy.migrate_repo.versions.001_austin.rst source/api/nova..db.sqlalchemy.migrate_repo.versions.002_bexar.rst source/api/nova..db.sqlalchemy.migrate_repo.versions.003_add_label_to_networks.rst source/api/nova..db.sqlalchemy.migrate_repo.versions.004_add_zone_tables.rst +source/api/nova..db.sqlalchemy.migrate_repo.versions.005_add_instance_metadata.rst +source/api/nova..db.sqlalchemy.migrate_repo.versions.006_add_provider_data_to_volumes.rst +source/api/nova..db.sqlalchemy.migrate_repo.versions.007_add_instance_types.rst source/api/nova..db.sqlalchemy.migration.rst source/api/nova..db.sqlalchemy.models.rst source/api/nova..db.sqlalchemy.session.rst @@ -98,6 +101,7 @@ source/api/nova..tests.test_compute.rst source/api/nova..tests.test_console.rst source/api/nova..tests.test_direct.rst source/api/nova..tests.test_flags.rst +source/api/nova..tests.test_instance_types.rst source/api/nova..tests.test_localization.rst source/api/nova..tests.test_log.rst source/api/nova..tests.test_middleware.rst @@ -107,7 +111,9 @@ source/api/nova..tests.test_quota.rst source/api/nova..tests.test_rpc.rst source/api/nova..tests.test_scheduler.rst source/api/nova..tests.test_service.rst +source/api/nova..tests.test_test.rst source/api/nova..tests.test_twistd.rst +source/api/nova..tests.test_utils.rst source/api/nova..tests.test_virt.rst source/api/nova..tests.test_volume.rst source/api/nova..tests.test_xenapi.rst @@ -134,273 +140,3 @@ source/api/nova..volume.manager.rst source/api/nova..volume.san.rst source/api/nova..wsgi.rst source/api/autoindex.rst -source/api/nova..adminclient.rst -source/api/nova..api.direct.rst -source/api/nova..api.ec2.admin.rst -source/api/nova..api.ec2.apirequest.rst -source/api/nova..api.ec2.cloud.rst -source/api/nova..api.ec2.metadatarequesthandler.rst -source/api/nova..api.openstack.auth.rst -source/api/nova..api.openstack.backup_schedules.rst -source/api/nova..api.openstack.common.rst -source/api/nova..api.openstack.consoles.rst -source/api/nova..api.openstack.faults.rst -source/api/nova..api.openstack.flavors.rst -source/api/nova..api.openstack.images.rst -source/api/nova..api.openstack.servers.rst -source/api/nova..api.openstack.shared_ip_groups.rst -source/api/nova..api.openstack.zones.rst -source/api/nova..auth.dbdriver.rst -source/api/nova..auth.fakeldap.rst -source/api/nova..auth.ldapdriver.rst -source/api/nova..auth.manager.rst -source/api/nova..auth.signer.rst -source/api/nova..cloudpipe.pipelib.rst -source/api/nova..compute.api.rst -source/api/nova..compute.instance_types.rst -source/api/nova..compute.manager.rst -source/api/nova..compute.monitor.rst -source/api/nova..compute.power_state.rst -source/api/nova..console.api.rst -source/api/nova..console.fake.rst -source/api/nova..console.manager.rst -source/api/nova..console.xvp.rst -source/api/nova..context.rst -source/api/nova..crypto.rst -source/api/nova..db.api.rst -source/api/nova..db.base.rst -source/api/nova..db.migration.rst -source/api/nova..db.sqlalchemy.api.rst -source/api/nova..db.sqlalchemy.migrate_repo.manage.rst -source/api/nova..db.sqlalchemy.migrate_repo.versions.001_austin.rst -source/api/nova..db.sqlalchemy.migrate_repo.versions.002_bexar.rst -source/api/nova..db.sqlalchemy.migrate_repo.versions.003_add_label_to_networks.rst -source/api/nova..db.sqlalchemy.migrate_repo.versions.004_add_zone_tables.rst -source/api/nova..db.sqlalchemy.migration.rst -source/api/nova..db.sqlalchemy.models.rst -source/api/nova..db.sqlalchemy.session.rst -source/api/nova..exception.rst -source/api/nova..fakememcache.rst -source/api/nova..fakerabbit.rst -source/api/nova..flags.rst -source/api/nova..image.glance.rst -source/api/nova..image.local.rst -source/api/nova..image.s3.rst -source/api/nova..image.service.rst -source/api/nova..log.rst -source/api/nova..manager.rst -source/api/nova..network.api.rst -source/api/nova..network.linux_net.rst -source/api/nova..network.manager.rst -source/api/nova..objectstore.bucket.rst -source/api/nova..objectstore.handler.rst -source/api/nova..objectstore.image.rst -source/api/nova..objectstore.stored.rst -source/api/nova..quota.rst -source/api/nova..rpc.rst -source/api/nova..scheduler.chance.rst -source/api/nova..scheduler.driver.rst -source/api/nova..scheduler.manager.rst -source/api/nova..scheduler.simple.rst -source/api/nova..scheduler.zone.rst -source/api/nova..service.rst -source/api/nova..test.rst -source/api/nova..tests.api.openstack.fakes.rst -source/api/nova..tests.api.openstack.test_adminapi.rst -source/api/nova..tests.api.openstack.test_api.rst -source/api/nova..tests.api.openstack.test_auth.rst -source/api/nova..tests.api.openstack.test_common.rst -source/api/nova..tests.api.openstack.test_faults.rst -source/api/nova..tests.api.openstack.test_flavors.rst -source/api/nova..tests.api.openstack.test_images.rst -source/api/nova..tests.api.openstack.test_ratelimiting.rst -source/api/nova..tests.api.openstack.test_servers.rst -source/api/nova..tests.api.openstack.test_shared_ip_groups.rst -source/api/nova..tests.api.openstack.test_zones.rst -source/api/nova..tests.api.test_wsgi.rst -source/api/nova..tests.db.fakes.rst -source/api/nova..tests.declare_flags.rst -source/api/nova..tests.fake_flags.rst -source/api/nova..tests.glance.stubs.rst -source/api/nova..tests.hyperv_unittest.rst -source/api/nova..tests.objectstore_unittest.rst -source/api/nova..tests.real_flags.rst -source/api/nova..tests.runtime_flags.rst -source/api/nova..tests.test_access.rst -source/api/nova..tests.test_api.rst -source/api/nova..tests.test_auth.rst -source/api/nova..tests.test_cloud.rst -source/api/nova..tests.test_compute.rst -source/api/nova..tests.test_console.rst -source/api/nova..tests.test_direct.rst -source/api/nova..tests.test_flags.rst -source/api/nova..tests.test_localization.rst -source/api/nova..tests.test_log.rst -source/api/nova..tests.test_middleware.rst -source/api/nova..tests.test_misc.rst -source/api/nova..tests.test_network.rst -source/api/nova..tests.test_quota.rst -source/api/nova..tests.test_rpc.rst -source/api/nova..tests.test_scheduler.rst -source/api/nova..tests.test_service.rst -source/api/nova..tests.test_twistd.rst -source/api/nova..tests.test_virt.rst -source/api/nova..tests.test_volume.rst -source/api/nova..tests.test_xenapi.rst -source/api/nova..tests.xenapi.stubs.rst -source/api/nova..twistd.rst -source/api/nova..utils.rst -source/api/nova..version.rst -source/api/nova..virt.connection.rst -source/api/nova..virt.disk.rst -source/api/nova..virt.fake.rst -source/api/nova..virt.hyperv.rst -source/api/nova..virt.images.rst -source/api/nova..virt.libvirt_conn.rst -source/api/nova..virt.xenapi.fake.rst -source/api/nova..virt.xenapi.network_utils.rst -source/api/nova..virt.xenapi.vm_utils.rst -source/api/nova..virt.xenapi.vmops.rst -source/api/nova..virt.xenapi.volume_utils.rst -source/api/nova..virt.xenapi.volumeops.rst -source/api/nova..virt.xenapi_conn.rst -source/api/nova..volume.api.rst -source/api/nova..volume.driver.rst -source/api/nova..volume.manager.rst -source/api/nova..volume.san.rst -source/api/nova..wsgi.rst -source/api/nova..adminclient.rst -source/api/nova..api.direct.rst -source/api/nova..api.ec2.admin.rst -source/api/nova..api.ec2.apirequest.rst -source/api/nova..api.ec2.cloud.rst -source/api/nova..api.ec2.metadatarequesthandler.rst -source/api/nova..api.openstack.auth.rst -source/api/nova..api.openstack.backup_schedules.rst -source/api/nova..api.openstack.common.rst -source/api/nova..api.openstack.consoles.rst -source/api/nova..api.openstack.faults.rst -source/api/nova..api.openstack.flavors.rst -source/api/nova..api.openstack.images.rst -source/api/nova..api.openstack.servers.rst -source/api/nova..api.openstack.shared_ip_groups.rst -source/api/nova..api.openstack.zones.rst -source/api/nova..auth.dbdriver.rst -source/api/nova..auth.fakeldap.rst -source/api/nova..auth.ldapdriver.rst -source/api/nova..auth.manager.rst -source/api/nova..auth.signer.rst -source/api/nova..cloudpipe.pipelib.rst -source/api/nova..compute.api.rst -source/api/nova..compute.instance_types.rst -source/api/nova..compute.manager.rst -source/api/nova..compute.monitor.rst -source/api/nova..compute.power_state.rst -source/api/nova..console.api.rst -source/api/nova..console.fake.rst -source/api/nova..console.manager.rst -source/api/nova..console.xvp.rst -source/api/nova..context.rst -source/api/nova..crypto.rst -source/api/nova..db.api.rst -source/api/nova..db.base.rst -source/api/nova..db.migration.rst -source/api/nova..db.sqlalchemy.api.rst -source/api/nova..db.sqlalchemy.migrate_repo.manage.rst -source/api/nova..db.sqlalchemy.migrate_repo.versions.001_austin.rst -source/api/nova..db.sqlalchemy.migrate_repo.versions.002_bexar.rst -source/api/nova..db.sqlalchemy.migrate_repo.versions.003_add_label_to_networks.rst -source/api/nova..db.sqlalchemy.migrate_repo.versions.004_add_zone_tables.rst -source/api/nova..db.sqlalchemy.migration.rst -source/api/nova..db.sqlalchemy.models.rst -source/api/nova..db.sqlalchemy.session.rst -source/api/nova..exception.rst -source/api/nova..fakememcache.rst -source/api/nova..fakerabbit.rst -source/api/nova..flags.rst -source/api/nova..image.glance.rst -source/api/nova..image.local.rst -source/api/nova..image.s3.rst -source/api/nova..image.service.rst -source/api/nova..log.rst -source/api/nova..manager.rst -source/api/nova..network.api.rst -source/api/nova..network.linux_net.rst -source/api/nova..network.manager.rst -source/api/nova..objectstore.bucket.rst -source/api/nova..objectstore.handler.rst -source/api/nova..objectstore.image.rst -source/api/nova..objectstore.stored.rst -source/api/nova..quota.rst -source/api/nova..rpc.rst -source/api/nova..scheduler.chance.rst -source/api/nova..scheduler.driver.rst -source/api/nova..scheduler.manager.rst -source/api/nova..scheduler.simple.rst -source/api/nova..scheduler.zone.rst -source/api/nova..service.rst -source/api/nova..test.rst -source/api/nova..tests.api.openstack.fakes.rst -source/api/nova..tests.api.openstack.test_adminapi.rst -source/api/nova..tests.api.openstack.test_api.rst -source/api/nova..tests.api.openstack.test_auth.rst -source/api/nova..tests.api.openstack.test_common.rst -source/api/nova..tests.api.openstack.test_faults.rst -source/api/nova..tests.api.openstack.test_flavors.rst -source/api/nova..tests.api.openstack.test_images.rst -source/api/nova..tests.api.openstack.test_ratelimiting.rst -source/api/nova..tests.api.openstack.test_servers.rst -source/api/nova..tests.api.openstack.test_shared_ip_groups.rst -source/api/nova..tests.api.openstack.test_zones.rst -source/api/nova..tests.api.test_wsgi.rst -source/api/nova..tests.db.fakes.rst -source/api/nova..tests.declare_flags.rst -source/api/nova..tests.fake_flags.rst -source/api/nova..tests.glance.stubs.rst -source/api/nova..tests.hyperv_unittest.rst -source/api/nova..tests.objectstore_unittest.rst -source/api/nova..tests.real_flags.rst -source/api/nova..tests.runtime_flags.rst -source/api/nova..tests.test_access.rst -source/api/nova..tests.test_api.rst -source/api/nova..tests.test_auth.rst -source/api/nova..tests.test_cloud.rst -source/api/nova..tests.test_compute.rst -source/api/nova..tests.test_console.rst -source/api/nova..tests.test_direct.rst -source/api/nova..tests.test_flags.rst -source/api/nova..tests.test_localization.rst -source/api/nova..tests.test_log.rst -source/api/nova..tests.test_middleware.rst -source/api/nova..tests.test_misc.rst -source/api/nova..tests.test_network.rst -source/api/nova..tests.test_quota.rst -source/api/nova..tests.test_rpc.rst -source/api/nova..tests.test_scheduler.rst -source/api/nova..tests.test_service.rst -source/api/nova..tests.test_twistd.rst -source/api/nova..tests.test_virt.rst -source/api/nova..tests.test_volume.rst -source/api/nova..tests.test_xenapi.rst -source/api/nova..tests.xenapi.stubs.rst -source/api/nova..twistd.rst -source/api/nova..utils.rst -source/api/nova..version.rst -source/api/nova..virt.connection.rst -source/api/nova..virt.disk.rst -source/api/nova..virt.fake.rst -source/api/nova..virt.hyperv.rst -source/api/nova..virt.images.rst -source/api/nova..virt.libvirt_conn.rst -source/api/nova..virt.xenapi.fake.rst -source/api/nova..virt.xenapi.network_utils.rst -source/api/nova..virt.xenapi.vm_utils.rst -source/api/nova..virt.xenapi.vmops.rst -source/api/nova..virt.xenapi.volume_utils.rst -source/api/nova..virt.xenapi.volumeops.rst -source/api/nova..virt.xenapi_conn.rst -source/api/nova..volume.api.rst -source/api/nova..volume.driver.rst -source/api/nova..volume.manager.rst -source/api/nova..volume.san.rst -source/api/nova..wsgi.rst -- cgit From fbcbf5ef805748bea29e4135ee8989830064c273 Mon Sep 17 00:00:00 2001 From: Josh Kearney Date: Tue, 1 Mar 2011 10:55:13 -0600 Subject: Fixed trunk merge issues --- nova/virt/xenapi/vm_utils.py | 2 +- nova/virt/xenapi/vmops.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/nova/virt/xenapi/vm_utils.py b/nova/virt/xenapi/vm_utils.py index 527d8700c..977c19359 100644 --- a/nova/virt/xenapi/vm_utils.py +++ b/nova/virt/xenapi/vm_utils.py @@ -343,7 +343,7 @@ class VMHelper(HelperBase): kwargs = {'params': pickle.dumps(params)} task = session.async_call_plugin('glance', 'download_vhd', kwargs) - vdi_uuid = session.wait_for_task(instance_id, task) + vdi_uuid = session.wait_for_task(task, instance_id) scan_sr(session, instance_id, sr_ref) diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py index 43b63bfd8..a3379fa7f 100644 --- a/nova/virt/xenapi/vmops.py +++ b/nova/virt/xenapi/vmops.py @@ -411,7 +411,7 @@ class VMOps(object): args = {'kernel-file': kernel, 'ramdisk-file': ramdisk} task = self._session.async_call_plugin( 'glance', 'remove_kernel_ramdisk', args) - self._session.wait_for_task(instance.id, task) + self._session.wait_for_task(task, instance.id) LOG.debug(_("kernel/ramdisk files removed")) -- cgit From 282a18a4c15f066e371596104f783f522309c5ee Mon Sep 17 00:00:00 2001 From: Ken Pepple Date: Tue, 1 Mar 2011 10:40:56 -0800 Subject: corrected copyrights for new files --- doc/source/runnova/managing.instance.types.rst | 4 +--- nova/compute/instance_types.py | 1 + nova/db/sqlalchemy/migrate_repo/versions/007_add_instance_types.py | 1 + nova/tests/test_instance_types.py | 1 + 4 files changed, 4 insertions(+), 3 deletions(-) diff --git a/doc/source/runnova/managing.instance.types.rst b/doc/source/runnova/managing.instance.types.rst index 37bb2e99e..746077716 100644 --- a/doc/source/runnova/managing.instance.types.rst +++ b/doc/source/runnova/managing.instance.types.rst @@ -1,7 +1,5 @@ .. - Copyright 2010-2011 United States Government as represented by the - Administrator of the National Aeronautics and Space Administration. - All Rights Reserved. + Copyright 2011 Ken Pepple 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 diff --git a/nova/compute/instance_types.py b/nova/compute/instance_types.py index ce4b25964..7d401fc86 100644 --- a/nova/compute/instance_types.py +++ b/nova/compute/instance_types.py @@ -4,6 +4,7 @@ # Administrator of the National Aeronautics and Space Administration. # All Rights Reserved. # Copyright (c) 2010 Citrix Systems, Inc. +# Copyright 2011 Ken Pepple # # 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 diff --git a/nova/db/sqlalchemy/migrate_repo/versions/007_add_instance_types.py b/nova/db/sqlalchemy/migrate_repo/versions/007_add_instance_types.py index fec191214..66609054e 100644 --- a/nova/db/sqlalchemy/migrate_repo/versions/007_add_instance_types.py +++ b/nova/db/sqlalchemy/migrate_repo/versions/007_add_instance_types.py @@ -1,5 +1,6 @@ # vim: tabstop=4 shiftwidth=4 softtabstop=4 +# Copyright 2011 Ken Pepple # 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 diff --git a/nova/tests/test_instance_types.py b/nova/tests/test_instance_types.py index 1b192ca28..edc538879 100644 --- a/nova/tests/test_instance_types.py +++ b/nova/tests/test_instance_types.py @@ -1,5 +1,6 @@ # vim: tabstop=4 shiftwidth=4 softtabstop=4 +# Copyright 2011 Ken Pepple # 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 -- cgit From 93b69176277217a3cfae738dd328e649081a370f Mon Sep 17 00:00:00 2001 From: Josh Kearney Date: Tue, 1 Mar 2011 13:26:31 -0600 Subject: Review feedback --- nova/compute/manager.py | 42 ++++++++++++++++++++++++------------------ nova/virt/xenapi/vmops.py | 42 ++++++++++++++++++++++-------------------- 2 files changed, 46 insertions(+), 38 deletions(-) diff --git a/nova/compute/manager.py b/nova/compute/manager.py index ef7ed55c9..3af97683f 100644 --- a/nova/compute/manager.py +++ b/nova/compute/manager.py @@ -370,16 +370,19 @@ class ComputeManager(manager.Manager): context = context.elevated() instance_ref = self.db.instance_get(context, instance_id) LOG.audit(_('instance %s: rescuing'), instance_id, context=context) - self.db.instance_set_state(context, - instance_id, - power_state.NOSTATE, - 'rescuing') + self.db.instance_set_state( + context, + instance_id, + power_state.NOSTATE, + 'rescuing') self.network_manager.setup_compute_network(context, instance_id) - self.driver.rescue(instance_ref, - lambda result: self._update_state_callback(self, - context, - instance_id, - result)) + self.driver.rescue( + instance_ref, + lambda result: self._update_state_callback( + self, + context, + instance_id, + result)) self._update_state(context, instance_id) @exception.wrap_exception @@ -389,15 +392,18 @@ class ComputeManager(manager.Manager): context = context.elevated() instance_ref = self.db.instance_get(context, instance_id) LOG.audit(_('instance %s: unrescuing'), instance_id, context=context) - self.db.instance_set_state(context, - instance_id, - power_state.NOSTATE, - 'unrescuing') - self.driver.unrescue(instance_ref, - lambda result: self._update_state_callback(self, - context, - instance_id, - result)) + self.db.instance_set_state( + context, + instance_id, + power_state.NOSTATE, + 'unrescuing') + self.driver.unrescue( + instance_ref, + lambda result: self._update_state_callback( + self, + context, + instance_id, + result)) self._update_state(context, instance_id) @staticmethod diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py index a3379fa7f..1c58f529d 100644 --- a/nova/virt/xenapi/vmops.py +++ b/nova/virt/xenapi/vmops.py @@ -202,18 +202,19 @@ class VMOps(object): _('Instance not present %s') % instance_name) return vm - def _bootlock(self, vm, unlock=False): + def _acquire_bootlock(self, vm): """Prevent an instance from booting""" - if unlock: - self._session.call_xenapi( - "VM.remove_from_blocked_operations", - vm, - "start") - else: - self._session.call_xenapi( - "VM.set_blocked_operations", - vm, - {"start": ""}) + self._session.call_xenapi( + "VM.set_blocked_operations", + vm, + {"start": ""}) + + def _release_bootlock(self, vm): + """Allow an instance to boot""" + self._session.call_xenapi( + "VM.remove_from_blocked_operations", + vm, + "start") def snapshot(self, instance, image_id): """ Create snapshot from a running VM instance @@ -338,7 +339,7 @@ class VMOps(object): raise RuntimeError(resp_dict['message']) return resp_dict['message'] - def _shutdown(self, instance, vm): + def _shutdown(self, instance, vm, hard=True): """Shutdown an instance""" state = self.get_info(instance['name'])['state'] if state == power_state.SHUTDOWN: @@ -350,12 +351,13 @@ class VMOps(object): LOG.debug(_("Shutting down VM for Instance %(instance_id)s") % locals()) try: - try: - task = self._session.call_xenapi("Async.VM.clean_shutdown", vm) - self._session.wait_for_task(task, instance.id) - except self.XenAPI.Failure: + task = None + if hard: task = self._session.call_xenapi("Async.VM.hard_shutdown", vm) - self._session.wait_for_task(task, instance.id) + else: + task = self._session.call_xenapi("Async.VM.clean_shutdown", vm) + + self._session.wait_for_task(task, instance.id) except self.XenAPI.Failure, exc: LOG.exception(exc) @@ -501,7 +503,7 @@ class VMOps(object): vm = self._get_vm_opaque_ref(instance) self._shutdown(instance, vm) - self._bootlock(vm) + self._acquire_bootlock(vm) instance._rescue = True self.spawn(instance) @@ -533,7 +535,7 @@ class VMOps(object): for vbd_ref in vbds: vbd = self._session.get_xenapi().VBD.get_record(vbd_ref) - if vbd["userdevice"] == str(1): + if vbd["userdevice"] == "1": VMHelper.unplug_vbd(self._session, vbd_ref) VMHelper.destroy_vbd(self._session, vbd_ref) @@ -551,7 +553,7 @@ class VMOps(object): task2 = self._session.call_xenapi('Async.VM.destroy', rescue_vm) self._session.wait_for_task(task2, instance.id) - self._bootlock(original_vm, unlock=True) + self._release_bootlock(original_vm) self._start(instance, original_vm) def get_info(self, instance): -- cgit From f617fc087367a3d65bd4b826bf735f65fec9d2fd Mon Sep 17 00:00:00 2001 From: Masanori Itoh Date: Thu, 3 Mar 2011 00:28:04 +0900 Subject: Change DescribeKeyPairs response tag from keypairsSet to keySet, and fix lp720133. --- nova/api/ec2/cloud.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nova/api/ec2/cloud.py b/nova/api/ec2/cloud.py index 844ccbe5e..c6309f03c 100644 --- a/nova/api/ec2/cloud.py +++ b/nova/api/ec2/cloud.py @@ -298,7 +298,7 @@ class CloudController(object): 'keyFingerprint': key_pair['fingerprint'], }) - return {'keypairsSet': result} + return {'keySet': result} def create_key_pair(self, context, key_name, **kwargs): LOG.audit(_("Create key pair %s"), key_name, context=context) -- cgit From 6d62f387e39b42821f8a8f6ca560dd47b3bb9c7e Mon Sep 17 00:00:00 2001 From: Josh Kearney Date: Wed, 2 Mar 2011 15:42:21 -0600 Subject: Inject IPv6 data into XenStore for instance --- nova/virt/xenapi/vmops.py | 28 ++++++++++++++++++++-------- 1 file changed, 20 insertions(+), 8 deletions(-) diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py index bc39aa140..094b41588 100644 --- a/nova/virt/xenapi/vmops.py +++ b/nova/virt/xenapi/vmops.py @@ -514,18 +514,30 @@ class VMOps(object): network_IPs = [ip for ip in IPs if ip.network_id == network.id] def ip_dict(ip): - return {'netmask': network['netmask'], - 'enabled': '1', - 'ip': ip.address} + return { + "ip": ip.address, + "netmask": network["netmask"], + "enabled": "1"} + + def ip6_dict(ip6): + return { + "ip": ip6.addressV6, + "netmask": ip6.netmaskV6, + "gateway": ip6.gatewayV6, + "enabled": "1"} mac_id = instance.mac_address.replace(':', '') location = 'vm-data/networking/%s' % mac_id - mapping = {'label': network['label'], - 'gateway': network['gateway'], - 'mac': instance.mac_address, - 'dns': [network['dns']], - 'ips': [ip_dict(ip) for ip in network_IPs]} + mapping = { + 'label': network['label'], + 'gateway': network['gateway'], + 'mac': instance.mac_address, + 'dns': [network['dns']], + 'ips': [ip_dict(ip) for ip in network_IPs], + 'ip6s': [ip6_dict(ip) for ip in network_IPs]} + self.write_to_param_xenstore(vm_opaque_ref, {location: mapping}) + try: self.write_to_xenstore(vm_opaque_ref, location, mapping['location']) -- cgit From 953fe68ce9b27322003200c464c121464761d1e2 Mon Sep 17 00:00:00 2001 From: Cerberus Date: Wed, 2 Mar 2011 15:46:50 -0600 Subject: merge fixes --- nova/compute/api.py | 4 +- nova/virt/xenapi/vm_utils.py | 4 +- nova/virt/xenapi/vmops.py | 48 ++++++++++------------ plugins/xenserver/xenapi/etc/xapi.d/plugins/glance | 2 +- 4 files changed, 27 insertions(+), 31 deletions(-) diff --git a/nova/compute/api.py b/nova/compute/api.py index 52d30c7f7..fb7ef1aed 100644 --- a/nova/compute/api.py +++ b/nova/compute/api.py @@ -430,7 +430,7 @@ class API(base.Base): migration_ref = self.db.migration_get_by_instance_and_status(context, instance_id, 'finished') if not migration_ref: - raise exception.Error(_("No finished migrations found for \ + raise exception.NotFound(_("No finished migrations found for \ instance")) params = {'migration_id': migration_ref['id']} @@ -444,7 +444,7 @@ class API(base.Base): migration_ref = self.db.migration_get_by_instance_and_status(context, instance_id, 'finished') if not migration_ref: - raise exception.Error(_("No finished migrations found for \ + raise exception.NotFound(_("No finished migrations found for \ instance")) instance_ref = self.db.instance_get(context, instance_id) params = {'migration_id': migration_ref['id']} diff --git a/nova/virt/xenapi/vm_utils.py b/nova/virt/xenapi/vm_utils.py index eea9bb0b9..3ee963fa7 100644 --- a/nova/virt/xenapi/vm_utils.py +++ b/nova/virt/xenapi/vm_utils.py @@ -310,8 +310,7 @@ class VMHelper(HelperBase): def get_sr_path(cls, session, sr_label='slices'): """ Finds the SR and then coerces it into a path on the dom0 file system """ - # TODO(mdietz): replace this with the flag once unified-images merges - return '/var/run/sr-mount/%s' % cls.get_sr(session, sr_label) + return FLAGS.xenapi_sr_base_path + cls.get_sr(session, sr_label) @classmethod def upload_image(cls, session, instance_id, vdi_uuids, image_id): @@ -649,6 +648,7 @@ class VMHelper(HelperBase): @classmethod def scan_default_sr(cls, session): """Looks for the system default SR and triggers a re-scan""" + #FIXME(sirp/mdietz): refactor scan_default_sr in there sr_ref = cls.get_sr(session) session.call_xenapi('SR.scan', sr_ref) diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py index 5157f18f1..b54084842 100644 --- a/nova/virt/xenapi/vmops.py +++ b/nova/virt/xenapi/vmops.py @@ -105,7 +105,7 @@ class VMOps(object): vdi_ref = self._session.call_xenapi('VDI.get_by_uuid', disk) if disk_image_type == ImageType.DISK_RAW: - #Have a look at the VDI and see if it has a PV kernel + # Have a look at the VDI and see if it has a PV kernel pv_kernel = VMHelper.lookup_image(self._session, instance.id, vdi_ref) elif disk_image_type == ImageType.DISK_VHD: @@ -113,7 +113,6 @@ class VMOps(object): # configurable as Windows will use HVM. pv_kernel = True - #Have a look at the VDI and see if it has a PV kernel if instance.kernel_id: kernel = VMHelper.fetch_image(self._session, instance.id, instance.kernel_id, user, project, ImageType.KERNEL_RAMDISK) @@ -240,29 +239,20 @@ class VMOps(object): that will bundle the VHDs together and then push the bundle into Glance. """ - - with self._get_snapshot(instance) as snapshot: + template_vm_ref = None + try: + template_vm_ref, template_vdi_uuids = self._get_snapshot(instance) # call plugin to ship snapshot off to glance VMHelper.upload_image( - self._session, instance.id, snapshot.vdi_uuids, image_id) + self._session, instance.id, template_vdi_uuids, image_id) + finally: + if template_vm_ref: + self.virt._destroy(self.instance, template_vm_ref, shutdown=False, + destroy_kernel_ramdisk=False) logging.debug(_("Finished snapshot and upload for VM %s"), instance) def _get_snapshot(self, instance): - class Snapshot(object): - def __init__(self, virt, instance, vm_ref, vdis): - self.instance = instance - self.vdi_uuids = vdis - self.virt = virt - self.vm_ref = vm_ref - - def __enter__(self): - return self - - def __exit__(self, type, value, traceback): - self.virt._destroy(self.instance, self.vm_ref, shutdown=False, - destroy_kernel_ramdisk=False) - #TODO(sirp): Add quiesce and VSS locking support when Windows support # is added @@ -273,8 +263,7 @@ class VMOps(object): try: template_vm_ref, template_vdi_uuids = VMHelper.create_snapshot( self._session, instance.id, vm_ref, label) - return Snapshot(self, instance, template_vm_ref, - template_vdi_uuids) + return template_vm_ref, template_vdi_uuids except self.XenAPI.Failure, exc: logging.error(_("Unable to Snapshot %(vm_ref)s: %(exc)s") % locals()) @@ -294,9 +283,11 @@ class VMOps(object): # from the snapshot creation base_copy_uuid = cow_uuid = None - with self._get_snapshot(instance) as snapshot: + template_vdi_uuids = template_vm_ref = None + try: # transfer the base copy - base_copy_uuid = snapshot.vdi_uuids[1] + template_vm_ref, template_vdi_uuids = self._get_snapshot(instance) + base_copy_uuid = template_vdi_uuids[1] vdi_ref, vm_vdi_rec = \ VMHelper.get_vdi_for_vm_safely(self._session, vm_ref) cow_uuid = vm_vdi_rec['uuid'] @@ -304,7 +295,7 @@ class VMOps(object): params = {'host': dest, 'vdi_uuid': base_copy_uuid, 'instance_id': instance.id, - 'sr_path': VMHelper.get_sr_path(self._session), } + 'sr_path': VMHelper.get_sr_path(self._session)} task = self._session.async_call_plugin('migration', 'transfer_vhd', {'params': pickle.dumps(params)}) @@ -322,6 +313,11 @@ class VMOps(object): {'params': pickle.dumps(params)}) self._session.wait_for_task(instance.id, task) + finally: + if template_vm_ref: + self.virt._destroy(self.instance, template_vm_ref, shutdown=False, + destroy_kernel_ramdisk=False) + # TODO(mdietz): we could also consider renaming these to something # sensible so we don't need to blindly pass around dictionaries return {'base_copy': base_copy_uuid, 'cow': cow_uuid} @@ -332,9 +328,9 @@ class VMOps(object): new_cow_uuid = str(uuid.uuid4()) params = {'instance_id': instance.id, 'old_base_copy_uuid': disk_info['base_copy'], - 'old_cow_uuid': disk_info['cow'], + 'old_cow_uuid': disk_info['cow'], 'new_base_copy_uuid': new_base_copy_uuid, - 'new_cow_uuid': new_cow_uuid, + 'new_cow_uuid': new_cow_uuid, 'sr_path': VMHelper.get_sr_path(self._session), } task = self._session.async_call_plugin('migration', diff --git a/plugins/xenserver/xenapi/etc/xapi.d/plugins/glance b/plugins/xenserver/xenapi/etc/xapi.d/plugins/glance index d08754c19..aa12d432a 100644 --- a/plugins/xenserver/xenapi/etc/xapi.d/plugins/glance +++ b/plugins/xenserver/xenapi/etc/xapi.d/plugins/glance @@ -207,7 +207,7 @@ def _upload_tarball(staging_path, image_id, glance_host, glance_port): 'transfer-encoding': 'chunked', 'x-image-meta-is_public': 'True', 'x-image-meta-status': 'queued', - 'x-image-meta-type': 'vhd', } + 'x-image-meta-type': 'vhd'} for header, value in headers.iteritems(): conn.putheader(header, value) conn.endheaders() -- cgit From 67d9051551775df73aed118a3ca307c61d284225 Mon Sep 17 00:00:00 2001 From: Josh Kearney Date: Wed, 2 Mar 2011 16:20:54 -0600 Subject: Added IPv6 migrations --- .../versions/007_add_ipv6_to_fixed_ips.py | 90 ++++++++++++++++++++++ nova/db/sqlalchemy/models.py | 3 + 2 files changed, 93 insertions(+) create mode 100644 nova/db/sqlalchemy/migrate_repo/versions/007_add_ipv6_to_fixed_ips.py diff --git a/nova/db/sqlalchemy/migrate_repo/versions/007_add_ipv6_to_fixed_ips.py b/nova/db/sqlalchemy/migrate_repo/versions/007_add_ipv6_to_fixed_ips.py new file mode 100644 index 000000000..427934d53 --- /dev/null +++ b/nova/db/sqlalchemy/migrate_repo/versions/007_add_ipv6_to_fixed_ips.py @@ -0,0 +1,90 @@ +# Copyright 2011 OpenStack LLC +# 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. + +from sqlalchemy import * +from migrate import * + +from nova import log as logging + + +meta = MetaData() + + +# Table stub-definitions +# Just for the ForeignKey and column creation to succeed, these are not the +# actual definitions of instances or services. +# +fixed_ips = Table( + "fixed_ips", + meta, + Column( + "id", + Integer(), + primary_key=True, + nullable=False)) + +# +# New Tables +# +# None + +# +# Tables to alter +# +# None + +# +# Columns to add to existing tables +# + +fixed_ips_addressV6 = Column( + "addressV6", + String( + length=255, + convert_unicode=False, + assert_unicode=None, + unicode_error=None, + _warn_on_bytestring=False)) + + +fixed_ips_netmaskV6 = Column( + "netmaskV6", + String( + length=3, + convert_unicode=False, + assert_unicode=None, + unicode_error=None, + _warn_on_bytestring=False)) + + +fixed_ips_gatewayV6 = Column( + "gatewayV6", + String( + length=255, + convert_unicode=False, + assert_unicode=None, + unicode_error=None, + _warn_on_bytestring=False)) + + +def upgrade(migrate_engine): + # Upgrade operations go here. Don't create your own engine; + # bind migrate_engine to your metadata + meta.bind = migrate_engine + + # Add columns to existing tables + fixed_ips.create_column(fixed_ips_addressV6) + fixed_ips.create_column(fixed_ips_netmaskV6) + fixed_ips.create_column(fixed_ips_gatewayV6) diff --git a/nova/db/sqlalchemy/models.py b/nova/db/sqlalchemy/models.py index 1882efeba..821fd4a83 100644 --- a/nova/db/sqlalchemy/models.py +++ b/nova/db/sqlalchemy/models.py @@ -437,6 +437,9 @@ class FixedIp(BASE, NovaBase): allocated = Column(Boolean, default=False) leased = Column(Boolean, default=False) reserved = Column(Boolean, default=False) + addressV6 = Column(String(255)) + netmaskV6 = Column(String(3)) + gatewayV6 = Column(String(255)) class User(BASE, NovaBase): -- cgit From e34e9dd982870915f8c4dbf84a08bece42b0c592 Mon Sep 17 00:00:00 2001 From: Josh Kearney Date: Wed, 2 Mar 2011 17:09:50 -0600 Subject: Updated docstrings --- nova/virt/xenapi/vmops.py | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py index 1c58f529d..10d5c0160 100644 --- a/nova/virt/xenapi/vmops.py +++ b/nova/virt/xenapi/vmops.py @@ -495,7 +495,12 @@ class VMOps(object): self._wait_with_callback(instance.id, task, callback) def rescue(self, instance, callback): - """Rescue the specified instance""" + """Rescue the specified instance + - shutdown the instance VM + - set 'bootlock' to prevent the instance from starting in rescue + - spawn a rescue VM (the vm name-label will be instance-N-rescue) + + """ rescue_vm = VMHelper.lookup(self._session, instance.name + "-rescue") if rescue_vm: raise RuntimeError(_( @@ -521,7 +526,12 @@ class VMOps(object): self._session.call_xenapi("Async.VBD.plug", vbd_ref) def unrescue(self, instance, callback): - """Unrescue the specified instance""" + """Unrescue the specified instance + - unplug the instance VM's disk from the rescue VM + - teardown the rescue VM + - release the bootlock to allow the instance VM to start + + """ rescue_vm = VMHelper.lookup(self._session, instance.name + "-rescue") if not rescue_vm: -- cgit From 077a77a1ab6fbec468b36e2975c1e185235c17ff Mon Sep 17 00:00:00 2001 From: Ken Pepple Date: Wed, 2 Mar 2011 15:49:51 -0800 Subject: removed create and delete method (and corresponding tests) from flavors.py --- nova/api/openstack/flavors.py | 20 -------------------- nova/tests/api/openstack/test_flavors.py | 14 ++------------ 2 files changed, 2 insertions(+), 32 deletions(-) diff --git a/nova/api/openstack/flavors.py b/nova/api/openstack/flavors.py index 375e12b18..4db812c2d 100644 --- a/nova/api/openstack/flavors.py +++ b/nova/api/openstack/flavors.py @@ -56,26 +56,6 @@ class Controller(wsgi.Controller): return dict(flavor=values) raise faults.Fault(exc.HTTPNotFound()) - def create(self, req): - """Create a flavor.""" - #TODO(jk0): Finish this later - #instance_types.create( - # name, - # memory, - # vcpus, - # local_gb, - # flavor_id, - # swap, - # rxtx_quota, - # rxtx_cap) - return "CREATE! %s" % req - - def delete(self, req, id): - """Delete a flavor.""" - #TODO(jk0): Finish this later - #instance_type.destroy(name) - return "DELETE! %s %s" % (req, id) - def _all_ids(self): """Return the list of all flavorids.""" # FIXME(kpepple) do we really need admin context here ? diff --git a/nova/tests/api/openstack/test_flavors.py b/nova/tests/api/openstack/test_flavors.py index 2626f92ba..319767bb5 100644 --- a/nova/tests/api/openstack/test_flavors.py +++ b/nova/tests/api/openstack/test_flavors.py @@ -46,17 +46,7 @@ class FlavorsTest(test.TestCase): res = req.get_response(fakes.wsgi_app()) self.assertEqual(res.status_int, 200) - def test_create_flavor(self): - req = webob.Request.blank("/v1.0/flavors") - req.method = "POST" - res = req.get_response(fakes.wsgi_app()) - self.assertEqual(res.status_int, 200) - - def test_delete_flavor(self): - req = webob.Request.blank("/v1.0/flavors/1") - req.method = "DELETE" + def test_get_flavor_by_id(self): + req = webob.Request.blank('/v1.0/flavors/1') res = req.get_response(fakes.wsgi_app()) self.assertEqual(res.status_int, 200) - - def test_get_flavor_by_id(self): - pass -- cgit From 092e26667b4cf7389cbbb0d206fe7721515262eb Mon Sep 17 00:00:00 2001 From: Ken Pepple Date: Wed, 2 Mar 2011 16:07:05 -0800 Subject: fixed coding style per devcamcar review notes --- bin/nova-manage | 37 ++++++++----------------------------- 1 file changed, 8 insertions(+), 29 deletions(-) diff --git a/bin/nova-manage b/bin/nova-manage index 0b71da41e..15bbe2f45 100755 --- a/bin/nova-manage +++ b/bin/nova-manage @@ -669,40 +669,19 @@ class InstanceTypeCommands(object): deleted = ('', ', inactive')[val["deleted"] == 1] print ("%s: Memory: %sMB, VCPUS: %s, Storage: %sGB, FlavorID: %s, " "Swap: %sGB, RXTX Quota: %sGB, RXTX Cap: %sMB%s") % ( - n, - val["memory_mb"], - val["vcpus"], - val["local_gb"], - val["flavorid"], - val["swap"], - val["rxtx_quota"], - val["rxtx_cap"], - deleted) - - def create( - self, - name, - memory, - vcpus, - local_gb, - flavorid, - swap=0, - rxtx_quota=0, - rxtx_cap=0): + n, val["memory_mb"], val["vcpus"], val["local_gb"], + val["flavorid"], val["swap"], val["rxtx_quota"], + val["rxtx_cap"], deleted) + + def create(self, name, memory, vcpus, local_gb, flavorid, + swap=0, rxtx_quota=0, rxtx_cap=0): """Creates instance types / flavors arguments: name memory vcpus local_gb flavorid [swap] [rxtx_quota] [rxtx_cap] """ try: - instance_types.create( - name, - memory, - vcpus, - local_gb, - flavorid, - swap, - rxtx_quota, - rxtx_cap) + instance_types.create(name, memory, vcpus, local_gb, + flavorid, swap, rxtx_quota, rxtx_cap) except exception.InvalidInputException: print "Must supply valid parameters to create instance type" print e -- cgit From 4243e8e2b41f1438023b1184b1281474b27b5467 Mon Sep 17 00:00:00 2001 From: Ken Pepple Date: Wed, 2 Mar 2011 16:09:27 -0800 Subject: coding style change per devcamcar review --- nova/compute/instance_types.py | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/nova/compute/instance_types.py b/nova/compute/instance_types.py index 7d401fc86..20cf1faee 100644 --- a/nova/compute/instance_types.py +++ b/nova/compute/instance_types.py @@ -30,15 +30,8 @@ from nova import exception FLAGS = flags.FLAGS -def create( - name, - memory, - vcpus, - local_gb, - flavorid, - swap=0, - rxtx_quota=0, - rxtx_cap=0): +def create(name, memory, vcpus, local_gb, flavorid, swap=0, + rxtx_quota=0,rxtx_cap=0): """Creates instance types / flavors arguments: name memory vcpus local_gb flavorid swap rxtx_quota rxtx_cap """ -- cgit From 0bf74ef365688476b2b3a44e353c0062989d33b5 Mon Sep 17 00:00:00 2001 From: Ken Pepple Date: Wed, 2 Mar 2011 16:12:22 -0800 Subject: fixed _context typo --- nova/api/ec2/admin.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/nova/api/ec2/admin.py b/nova/api/ec2/admin.py index 51a06bb26..d9a4ef999 100644 --- a/nova/api/ec2/admin.py +++ b/nova/api/ec2/admin.py @@ -114,9 +114,9 @@ class AdminController(object): def __str__(self): return 'AdminController' - def describe_instance_types(self, _context, **_kwargs): + def describe_instance_types(self, context, **_kwargs): """Returns all active instance types data (vcpus, memory, etc.)""" - return {'instanceTypeSet': [db.instance_type_get_all(_context)]} + return {'instanceTypeSet': [db.instance_type_get_all(context)]} def describe_user(self, _context, name, **_kwargs): """Returns user data, including access and secret keys.""" -- cgit From 22ec4e190ccf9e30a7862e1ee7d90f2a0858c438 Mon Sep 17 00:00:00 2001 From: Ken Pepple Date: Wed, 2 Mar 2011 16:12:36 -0800 Subject: pep8 --- nova/compute/instance_types.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nova/compute/instance_types.py b/nova/compute/instance_types.py index 20cf1faee..f360a7ac3 100644 --- a/nova/compute/instance_types.py +++ b/nova/compute/instance_types.py @@ -31,7 +31,7 @@ FLAGS = flags.FLAGS def create(name, memory, vcpus, local_gb, flavorid, swap=0, - rxtx_quota=0,rxtx_cap=0): + rxtx_quota=0, rxtx_cap=0): """Creates instance types / flavors arguments: name memory vcpus local_gb flavorid swap rxtx_quota rxtx_cap """ -- cgit From 86aed7edae3dd90741d0da704a99460701b8bcc7 Mon Sep 17 00:00:00 2001 From: Ken Pepple Date: Wed, 2 Mar 2011 16:32:09 -0800 Subject: added in req.environ for context --- nova/api/openstack/flavors.py | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/nova/api/openstack/flavors.py b/nova/api/openstack/flavors.py index 4db812c2d..f3d040ba3 100644 --- a/nova/api/openstack/flavors.py +++ b/nova/api/openstack/flavors.py @@ -41,25 +41,19 @@ class Controller(wsgi.Controller): def detail(self, req): """Return all flavors in detail.""" - items = [self.show(req, id)['flavor'] for id in self._all_ids()] + items = [self.show(req, id)['flavor'] for id in self._all_ids(req)] return dict(flavors=items) def show(self, req, id): """Return data about the given flavor id.""" - # FIXME(kpepple) do we really need admin context here ? - ctxt = context.get_admin_context() + ctxt = req.environ['nova.context'] values = db.instance_type_get_by_flavor_id(ctxt, id) - # FIXME(kpepple) refactor db call to return dict - # v = val.values()[0] - # item = dict(ram=v['memory_mb'], disk=v['local_gb'], - # id=v['flavorid'], name=val.keys()[0]) return dict(flavor=values) raise faults.Fault(exc.HTTPNotFound()) - def _all_ids(self): + def _all_ids(self, req): """Return the list of all flavorids.""" - # FIXME(kpepple) do we really need admin context here ? - ctxt = context.get_admin_context() + ctxt = req.environ['nova.context'] inst_types = db.instance_type_get_all(ctxt) flavor_ids = [inst_types[i]['flavorid'] for i in inst_types.keys()] - return flavor_ids + return sorted(flavor_ids) -- cgit From 7305bc9a47c03d9b471d747341ca3e95e89f56f4 Mon Sep 17 00:00:00 2001 From: Ken Pepple Date: Wed, 2 Mar 2011 16:35:53 -0800 Subject: pep8 --- bin/nova-manage | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bin/nova-manage b/bin/nova-manage index 15bbe2f45..9bf3a1bb3 100755 --- a/bin/nova-manage +++ b/bin/nova-manage @@ -673,7 +673,7 @@ class InstanceTypeCommands(object): val["flavorid"], val["swap"], val["rxtx_quota"], val["rxtx_cap"], deleted) - def create(self, name, memory, vcpus, local_gb, flavorid, + def create(self, name, memory, vcpus, local_gb, flavorid, swap=0, rxtx_quota=0, rxtx_cap=0): """Creates instance types / flavors arguments: name memory vcpus local_gb flavorid [swap] [rxtx_quota] -- cgit From 28896fcfb474662fe339fa5b05aec33b3896b4fa Mon Sep 17 00:00:00 2001 From: Ken Pepple Date: Wed, 2 Mar 2011 16:37:02 -0800 Subject: changed _context --- nova/db/sqlalchemy/api.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nova/db/sqlalchemy/api.py b/nova/db/sqlalchemy/api.py index f4cd16d9a..919dda118 100644 --- a/nova/db/sqlalchemy/api.py +++ b/nova/db/sqlalchemy/api.py @@ -2077,7 +2077,7 @@ def console_get(context, console_id, instance_id=None): @require_admin_context -def instance_type_create(context, values): +def instance_type_create(_context, values): try: instance_type_ref = models.InstanceTypes() instance_type_ref.update(values) -- cgit From 1abd891f65ea8291dc0c3f2075de80dc92c0d431 Mon Sep 17 00:00:00 2001 From: Ken Pepple Date: Wed, 2 Mar 2011 16:46:32 -0800 Subject: corrected error message --- nova/compute/instance_types.py | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) diff --git a/nova/compute/instance_types.py b/nova/compute/instance_types.py index f360a7ac3..d31d3c304 100644 --- a/nova/compute/instance_types.py +++ b/nova/compute/instance_types.py @@ -58,8 +58,7 @@ def create(name, memory, vcpus, local_gb, flavorid, swap=0, rxtx_quota=rxtx_quota, rxtx_cap=rxtx_cap)) except exception.DBError: - raise exception.ApiError(_("Cannot create instance type: %s"), - name) + raise exception.ApiError(_("Cannot create instance type: %s" % name)) def destroy(name): @@ -71,8 +70,7 @@ def destroy(name): try: db.instance_type_destroy(context.get_admin_context(), name) except exception.NotFound: - raise exception.ApiError(_("Unknown instance type: %s"), - name) + raise exception.ApiError(_("Unknown instance type: %s" % name)) def purge(name): @@ -84,8 +82,7 @@ def purge(name): try: db.instance_type_purge(context.get_admin_context(), name) except exception.NotFound: - raise exception.ApiError(_("Unknown instance type: %s"), - name) + raise exception.ApiError(_("Unknown instance type: %s" % name)) def get_all_types(inactive=0): @@ -109,8 +106,7 @@ def get_instance_type(name): inst_type = db.instance_type_get_by_name(ctxt, name) return inst_type except exception.DBError: - raise exception.ApiError(_("Unknown instance type: %s"), - name) + raise exception.ApiError(_("Unknown instance type: %s" % name)) def get_by_type(instance_type): @@ -123,8 +119,8 @@ def get_by_type(instance_type): inst_type = db.instance_type_get_by_name(ctxt, instance_type) return inst_type['name'] except exception.DBError: - raise exception.ApiError(_("Unknown instance type: %s"), - instance_type) + raise exception.ApiError(_("Unknown instance type: %s" %\ + instance_type)) def get_by_flavor_id(flavor_id): @@ -136,5 +132,4 @@ def get_by_flavor_id(flavor_id): flavor = db.instance_type_get_by_flavor_id(ctxt, flavor_id) return flavor['name'] except exception.DBError: - raise exception.ApiError(_("Unknown flavor: %s"), - flavor_id) + raise exception.ApiError(_("Unknown flavor: %s" % flavor_id)) -- cgit From b39a3f099a4410c95658334fc1907a8eb6b5a5dc Mon Sep 17 00:00:00 2001 From: Ken Pepple Date: Wed, 2 Mar 2011 16:57:56 -0800 Subject: adding new source docs --- doc/.autogenerated | 141 +++++++++++++++++++++ ...ate_repo.versions.005_add_instance_metadata.rst | 6 + ...o.versions.006_add_provider_data_to_volumes.rst | 6 + ...igrate_repo.versions.007_add_instance_types.rst | 6 + doc/source/api/nova..tests.test_instance_types.rst | 6 + doc/source/api/nova..tests.test_test.rst | 6 + doc/source/api/nova..tests.test_utils.rst | 6 + 7 files changed, 177 insertions(+) create mode 100644 doc/source/api/nova..db.sqlalchemy.migrate_repo.versions.005_add_instance_metadata.rst create mode 100644 doc/source/api/nova..db.sqlalchemy.migrate_repo.versions.006_add_provider_data_to_volumes.rst create mode 100644 doc/source/api/nova..db.sqlalchemy.migrate_repo.versions.007_add_instance_types.rst create mode 100644 doc/source/api/nova..tests.test_instance_types.rst create mode 100644 doc/source/api/nova..tests.test_test.rst create mode 100644 doc/source/api/nova..tests.test_utils.rst diff --git a/doc/.autogenerated b/doc/.autogenerated index b11735994..456c8ad1e 100644 --- a/doc/.autogenerated +++ b/doc/.autogenerated @@ -140,3 +140,144 @@ source/api/nova..volume.manager.rst source/api/nova..volume.san.rst source/api/nova..wsgi.rst source/api/autoindex.rst +source/api/nova..adminclient.rst +source/api/nova..api.direct.rst +source/api/nova..api.ec2.admin.rst +source/api/nova..api.ec2.apirequest.rst +source/api/nova..api.ec2.cloud.rst +source/api/nova..api.ec2.metadatarequesthandler.rst +source/api/nova..api.openstack.auth.rst +source/api/nova..api.openstack.backup_schedules.rst +source/api/nova..api.openstack.common.rst +source/api/nova..api.openstack.consoles.rst +source/api/nova..api.openstack.faults.rst +source/api/nova..api.openstack.flavors.rst +source/api/nova..api.openstack.images.rst +source/api/nova..api.openstack.servers.rst +source/api/nova..api.openstack.shared_ip_groups.rst +source/api/nova..api.openstack.zones.rst +source/api/nova..auth.dbdriver.rst +source/api/nova..auth.fakeldap.rst +source/api/nova..auth.ldapdriver.rst +source/api/nova..auth.manager.rst +source/api/nova..auth.signer.rst +source/api/nova..cloudpipe.pipelib.rst +source/api/nova..compute.api.rst +source/api/nova..compute.instance_types.rst +source/api/nova..compute.manager.rst +source/api/nova..compute.monitor.rst +source/api/nova..compute.power_state.rst +source/api/nova..console.api.rst +source/api/nova..console.fake.rst +source/api/nova..console.manager.rst +source/api/nova..console.xvp.rst +source/api/nova..context.rst +source/api/nova..crypto.rst +source/api/nova..db.api.rst +source/api/nova..db.base.rst +source/api/nova..db.migration.rst +source/api/nova..db.sqlalchemy.api.rst +source/api/nova..db.sqlalchemy.migrate_repo.manage.rst +source/api/nova..db.sqlalchemy.migrate_repo.versions.001_austin.rst +source/api/nova..db.sqlalchemy.migrate_repo.versions.002_bexar.rst +source/api/nova..db.sqlalchemy.migrate_repo.versions.003_add_label_to_networks.rst +source/api/nova..db.sqlalchemy.migrate_repo.versions.004_add_zone_tables.rst +source/api/nova..db.sqlalchemy.migrate_repo.versions.005_add_instance_metadata.rst +source/api/nova..db.sqlalchemy.migrate_repo.versions.006_add_provider_data_to_volumes.rst +source/api/nova..db.sqlalchemy.migrate_repo.versions.007_add_instance_types.rst +source/api/nova..db.sqlalchemy.migration.rst +source/api/nova..db.sqlalchemy.models.rst +source/api/nova..db.sqlalchemy.session.rst +source/api/nova..exception.rst +source/api/nova..fakememcache.rst +source/api/nova..fakerabbit.rst +source/api/nova..flags.rst +source/api/nova..image.glance.rst +source/api/nova..image.local.rst +source/api/nova..image.s3.rst +source/api/nova..image.service.rst +source/api/nova..log.rst +source/api/nova..manager.rst +source/api/nova..network.api.rst +source/api/nova..network.linux_net.rst +source/api/nova..network.manager.rst +source/api/nova..objectstore.bucket.rst +source/api/nova..objectstore.handler.rst +source/api/nova..objectstore.image.rst +source/api/nova..objectstore.stored.rst +source/api/nova..quota.rst +source/api/nova..rpc.rst +source/api/nova..scheduler.chance.rst +source/api/nova..scheduler.driver.rst +source/api/nova..scheduler.manager.rst +source/api/nova..scheduler.simple.rst +source/api/nova..scheduler.zone.rst +source/api/nova..service.rst +source/api/nova..test.rst +source/api/nova..tests.api.openstack.fakes.rst +source/api/nova..tests.api.openstack.test_adminapi.rst +source/api/nova..tests.api.openstack.test_api.rst +source/api/nova..tests.api.openstack.test_auth.rst +source/api/nova..tests.api.openstack.test_common.rst +source/api/nova..tests.api.openstack.test_faults.rst +source/api/nova..tests.api.openstack.test_flavors.rst +source/api/nova..tests.api.openstack.test_images.rst +source/api/nova..tests.api.openstack.test_ratelimiting.rst +source/api/nova..tests.api.openstack.test_servers.rst +source/api/nova..tests.api.openstack.test_shared_ip_groups.rst +source/api/nova..tests.api.openstack.test_zones.rst +source/api/nova..tests.api.test_wsgi.rst +source/api/nova..tests.db.fakes.rst +source/api/nova..tests.declare_flags.rst +source/api/nova..tests.fake_flags.rst +source/api/nova..tests.glance.stubs.rst +source/api/nova..tests.hyperv_unittest.rst +source/api/nova..tests.objectstore_unittest.rst +source/api/nova..tests.real_flags.rst +source/api/nova..tests.runtime_flags.rst +source/api/nova..tests.test_access.rst +source/api/nova..tests.test_api.rst +source/api/nova..tests.test_auth.rst +source/api/nova..tests.test_cloud.rst +source/api/nova..tests.test_compute.rst +source/api/nova..tests.test_console.rst +source/api/nova..tests.test_direct.rst +source/api/nova..tests.test_flags.rst +source/api/nova..tests.test_instance_types.rst +source/api/nova..tests.test_localization.rst +source/api/nova..tests.test_log.rst +source/api/nova..tests.test_middleware.rst +source/api/nova..tests.test_misc.rst +source/api/nova..tests.test_network.rst +source/api/nova..tests.test_quota.rst +source/api/nova..tests.test_rpc.rst +source/api/nova..tests.test_scheduler.rst +source/api/nova..tests.test_service.rst +source/api/nova..tests.test_test.rst +source/api/nova..tests.test_twistd.rst +source/api/nova..tests.test_utils.rst +source/api/nova..tests.test_virt.rst +source/api/nova..tests.test_volume.rst +source/api/nova..tests.test_xenapi.rst +source/api/nova..tests.xenapi.stubs.rst +source/api/nova..twistd.rst +source/api/nova..utils.rst +source/api/nova..version.rst +source/api/nova..virt.connection.rst +source/api/nova..virt.disk.rst +source/api/nova..virt.fake.rst +source/api/nova..virt.hyperv.rst +source/api/nova..virt.images.rst +source/api/nova..virt.libvirt_conn.rst +source/api/nova..virt.xenapi.fake.rst +source/api/nova..virt.xenapi.network_utils.rst +source/api/nova..virt.xenapi.vm_utils.rst +source/api/nova..virt.xenapi.vmops.rst +source/api/nova..virt.xenapi.volume_utils.rst +source/api/nova..virt.xenapi.volumeops.rst +source/api/nova..virt.xenapi_conn.rst +source/api/nova..volume.api.rst +source/api/nova..volume.driver.rst +source/api/nova..volume.manager.rst +source/api/nova..volume.san.rst +source/api/nova..wsgi.rst diff --git a/doc/source/api/nova..db.sqlalchemy.migrate_repo.versions.005_add_instance_metadata.rst b/doc/source/api/nova..db.sqlalchemy.migrate_repo.versions.005_add_instance_metadata.rst new file mode 100644 index 000000000..cef0c243e --- /dev/null +++ b/doc/source/api/nova..db.sqlalchemy.migrate_repo.versions.005_add_instance_metadata.rst @@ -0,0 +1,6 @@ +The :mod:`nova..db.sqlalchemy.migrate_repo.versions.005_add_instance_metadata` Module +============================================================================== +.. automodule:: nova..db.sqlalchemy.migrate_repo.versions.005_add_instance_metadata + :members: + :undoc-members: + :show-inheritance: diff --git a/doc/source/api/nova..db.sqlalchemy.migrate_repo.versions.006_add_provider_data_to_volumes.rst b/doc/source/api/nova..db.sqlalchemy.migrate_repo.versions.006_add_provider_data_to_volumes.rst new file mode 100644 index 000000000..a15697196 --- /dev/null +++ b/doc/source/api/nova..db.sqlalchemy.migrate_repo.versions.006_add_provider_data_to_volumes.rst @@ -0,0 +1,6 @@ +The :mod:`nova..db.sqlalchemy.migrate_repo.versions.006_add_provider_data_to_volumes` Module +============================================================================== +.. automodule:: nova..db.sqlalchemy.migrate_repo.versions.006_add_provider_data_to_volumes + :members: + :undoc-members: + :show-inheritance: diff --git a/doc/source/api/nova..db.sqlalchemy.migrate_repo.versions.007_add_instance_types.rst b/doc/source/api/nova..db.sqlalchemy.migrate_repo.versions.007_add_instance_types.rst new file mode 100644 index 000000000..38842d1af --- /dev/null +++ b/doc/source/api/nova..db.sqlalchemy.migrate_repo.versions.007_add_instance_types.rst @@ -0,0 +1,6 @@ +The :mod:`nova..db.sqlalchemy.migrate_repo.versions.007_add_instance_types` Module +============================================================================== +.. automodule:: nova..db.sqlalchemy.migrate_repo.versions.007_add_instance_types + :members: + :undoc-members: + :show-inheritance: diff --git a/doc/source/api/nova..tests.test_instance_types.rst b/doc/source/api/nova..tests.test_instance_types.rst new file mode 100644 index 000000000..ebe689966 --- /dev/null +++ b/doc/source/api/nova..tests.test_instance_types.rst @@ -0,0 +1,6 @@ +The :mod:`nova..tests.test_instance_types` Module +============================================================================== +.. automodule:: nova..tests.test_instance_types + :members: + :undoc-members: + :show-inheritance: diff --git a/doc/source/api/nova..tests.test_test.rst b/doc/source/api/nova..tests.test_test.rst new file mode 100644 index 000000000..389eb3c99 --- /dev/null +++ b/doc/source/api/nova..tests.test_test.rst @@ -0,0 +1,6 @@ +The :mod:`nova..tests.test_test` Module +============================================================================== +.. automodule:: nova..tests.test_test + :members: + :undoc-members: + :show-inheritance: diff --git a/doc/source/api/nova..tests.test_utils.rst b/doc/source/api/nova..tests.test_utils.rst new file mode 100644 index 000000000..d61a7021f --- /dev/null +++ b/doc/source/api/nova..tests.test_utils.rst @@ -0,0 +1,6 @@ +The :mod:`nova..tests.test_utils` Module +============================================================================== +.. automodule:: nova..tests.test_utils + :members: + :undoc-members: + :show-inheritance: -- cgit From 45662001c477bdce7cd50b4f7f67e06479c3cbd3 Mon Sep 17 00:00:00 2001 From: Ken Pepple Date: Wed, 2 Mar 2011 16:59:38 -0800 Subject: requested style change --- nova/compute/instance_types.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/nova/compute/instance_types.py b/nova/compute/instance_types.py index d31d3c304..09b42f319 100644 --- a/nova/compute/instance_types.py +++ b/nova/compute/instance_types.py @@ -48,8 +48,7 @@ def create(name, memory, vcpus, local_gb, flavorid, swap=0, try: db.instance_type_create( context.get_admin_context(), - dict( - name=name, + dict(name=name, memory_mb=memory, vcpus=vcpus, local_gb=local_gb, -- cgit From 507a13d8dcdc11ea7638c8904d6d0de22d8e109b Mon Sep 17 00:00:00 2001 From: Ken Pepple Date: Wed, 2 Mar 2011 17:14:51 -0800 Subject: added logging to instance_types for DB errors per code review --- nova/compute/instance_types.py | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/nova/compute/instance_types.py b/nova/compute/instance_types.py index 09b42f319..79c879e8e 100644 --- a/nova/compute/instance_types.py +++ b/nova/compute/instance_types.py @@ -24,10 +24,12 @@ The built-in instance properties. from nova import context from nova import db -from nova import flags from nova import exception +from nova import flags +from nova import log as logging FLAGS = flags.FLAGS +LOG = logging.getLogger('nova.instance_types') def create(name, memory, vcpus, local_gb, flavorid, swap=0, @@ -56,7 +58,8 @@ def create(name, memory, vcpus, local_gb, flavorid, swap=0, swap=swap, rxtx_quota=rxtx_quota, rxtx_cap=rxtx_cap)) - except exception.DBError: + except exception.DBError, e: + LOG.exception(_('DB error: %s' % e)) raise exception.ApiError(_("Cannot create instance type: %s" % name)) @@ -69,6 +72,7 @@ def destroy(name): try: db.instance_type_destroy(context.get_admin_context(), name) except exception.NotFound: + LOG.exception(_('Instance type %s not found for deletion' % name)) raise exception.ApiError(_("Unknown instance type: %s" % name)) @@ -81,6 +85,7 @@ def purge(name): try: db.instance_type_purge(context.get_admin_context(), name) except exception.NotFound: + LOG.exception(_('Instance type %s not found for purge' % name)) raise exception.ApiError(_("Unknown instance type: %s" % name)) @@ -117,7 +122,8 @@ def get_by_type(instance_type): ctxt = context.get_admin_context() inst_type = db.instance_type_get_by_name(ctxt, instance_type) return inst_type['name'] - except exception.DBError: + except exception.DBError, e: + LOG.exception(_('DB error: %s' % e)) raise exception.ApiError(_("Unknown instance type: %s" %\ instance_type)) @@ -130,5 +136,6 @@ def get_by_flavor_id(flavor_id): ctxt = context.get_admin_context() flavor = db.instance_type_get_by_flavor_id(ctxt, flavor_id) return flavor['name'] - except exception.DBError: + except exception.DBError, e: + LOG.exception(_('DB error: %s' % e)) raise exception.ApiError(_("Unknown flavor: %s" % flavor_id)) -- cgit From 74f2a7537e9e4b8259a4179adc21eef59e59d3c5 Mon Sep 17 00:00:00 2001 From: Ken Pepple Date: Wed, 2 Mar 2011 17:38:42 -0800 Subject: catching bare except: --- nova/compute/instance_types.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nova/compute/instance_types.py b/nova/compute/instance_types.py index 79c879e8e..fa02a5dfa 100644 --- a/nova/compute/instance_types.py +++ b/nova/compute/instance_types.py @@ -40,7 +40,7 @@ def create(name, memory, vcpus, local_gb, flavorid, swap=0, for option in [memory, vcpus, local_gb, flavorid]: try: int(option) - except: + except ValueError: raise exception.InvalidInputException( _("create arguments must be positive integers")) if (int(memory) <= 0) or (int(vcpus) <= 0) or (int(local_gb) < 0): -- cgit From dc8e308819fb383b317ff866288965a27016557e Mon Sep 17 00:00:00 2001 From: Ken Pepple Date: Wed, 2 Mar 2011 17:54:04 -0800 Subject: moved migration to 008 (sigh) --- .../versions/007_add_instance_types.py | 87 ---------------------- .../versions/008_add_instance_types.py | 87 ++++++++++++++++++++++ 2 files changed, 87 insertions(+), 87 deletions(-) delete mode 100644 nova/db/sqlalchemy/migrate_repo/versions/007_add_instance_types.py create mode 100644 nova/db/sqlalchemy/migrate_repo/versions/008_add_instance_types.py diff --git a/nova/db/sqlalchemy/migrate_repo/versions/007_add_instance_types.py b/nova/db/sqlalchemy/migrate_repo/versions/007_add_instance_types.py deleted file mode 100644 index 66609054e..000000000 --- a/nova/db/sqlalchemy/migrate_repo/versions/007_add_instance_types.py +++ /dev/null @@ -1,87 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright 2011 Ken Pepple -# 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. - -from sqlalchemy import * -from migrate import * - -from nova import api -from nova import db -from nova import log as logging - -import datetime - -meta = MetaData() - - -# -# New Tables -# -instance_types = Table('instance_types', meta, - Column('created_at', DateTime(timezone=False)), - Column('updated_at', DateTime(timezone=False)), - Column('deleted_at', DateTime(timezone=False)), - Column('deleted', Boolean(create_constraint=True, name=None)), - Column('name', - String(length=255, convert_unicode=False, assert_unicode=None, - unicode_error=None, _warn_on_bytestring=False), - unique=True), - Column('id', Integer(), primary_key=True, nullable=False), - Column('memory_mb', Integer(), nullable=False), - Column('vcpus', Integer(), nullable=False), - Column('local_gb', Integer(), nullable=False), - Column('flavorid', Integer(), nullable=False, unique=True), - Column('swap', Integer(), nullable=False, default=0), - Column('rxtx_quota', Integer(), nullable=False, default=0), - Column('rxtx_cap', Integer(), nullable=False, default=0)) - - -def upgrade(migrate_engine): - # Upgrade operations go here - # Don't create your own engine; bind migrate_engine - # to your metadata - meta.bind = migrate_engine - try: - instance_types.create() - except Exception: - logging.info(repr(table)) - logging.exception('Exception while creating instance_types table') - raise - - # Here are the old static instance types - INSTANCE_TYPES = { - 'm1.tiny': dict(memory_mb=512, vcpus=1, local_gb=0, flavorid=1), - 'm1.small': dict(memory_mb=2048, vcpus=1, local_gb=20, flavorid=2), - 'm1.medium': dict(memory_mb=4096, vcpus=2, local_gb=40, flavorid=3), - 'm1.large': dict(memory_mb=8192, vcpus=4, local_gb=80, flavorid=4), - 'm1.xlarge': dict(memory_mb=16384, vcpus=8, local_gb=160, flavorid=5)} - try: - i = instance_types.insert() - for name, values in INSTANCE_TYPES.iteritems(): - # FIXME(kpepple) should we be seeding created_at / updated_at ? - # now = datetime.datatime.utcnow() - i.execute({'name': name, 'memory_mb': values["memory_mb"], - 'vcpus': values["vcpus"], 'deleted': 0, - 'local_gb': values["local_gb"], - 'flavorid': values["flavorid"]}) - except Exception: - logging.info(repr(table)) - logging.exception('Exception while seeding instance_types table') - raise - - -def downgrade(migrate_engine): - # Operations to reverse the above upgrade go here. - for table in (instance_types): - table.drop() diff --git a/nova/db/sqlalchemy/migrate_repo/versions/008_add_instance_types.py b/nova/db/sqlalchemy/migrate_repo/versions/008_add_instance_types.py new file mode 100644 index 000000000..66609054e --- /dev/null +++ b/nova/db/sqlalchemy/migrate_repo/versions/008_add_instance_types.py @@ -0,0 +1,87 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright 2011 Ken Pepple +# 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. + +from sqlalchemy import * +from migrate import * + +from nova import api +from nova import db +from nova import log as logging + +import datetime + +meta = MetaData() + + +# +# New Tables +# +instance_types = Table('instance_types', meta, + Column('created_at', DateTime(timezone=False)), + Column('updated_at', DateTime(timezone=False)), + Column('deleted_at', DateTime(timezone=False)), + Column('deleted', Boolean(create_constraint=True, name=None)), + Column('name', + String(length=255, convert_unicode=False, assert_unicode=None, + unicode_error=None, _warn_on_bytestring=False), + unique=True), + Column('id', Integer(), primary_key=True, nullable=False), + Column('memory_mb', Integer(), nullable=False), + Column('vcpus', Integer(), nullable=False), + Column('local_gb', Integer(), nullable=False), + Column('flavorid', Integer(), nullable=False, unique=True), + Column('swap', Integer(), nullable=False, default=0), + Column('rxtx_quota', Integer(), nullable=False, default=0), + Column('rxtx_cap', Integer(), nullable=False, default=0)) + + +def upgrade(migrate_engine): + # Upgrade operations go here + # Don't create your own engine; bind migrate_engine + # to your metadata + meta.bind = migrate_engine + try: + instance_types.create() + except Exception: + logging.info(repr(table)) + logging.exception('Exception while creating instance_types table') + raise + + # Here are the old static instance types + INSTANCE_TYPES = { + 'm1.tiny': dict(memory_mb=512, vcpus=1, local_gb=0, flavorid=1), + 'm1.small': dict(memory_mb=2048, vcpus=1, local_gb=20, flavorid=2), + 'm1.medium': dict(memory_mb=4096, vcpus=2, local_gb=40, flavorid=3), + 'm1.large': dict(memory_mb=8192, vcpus=4, local_gb=80, flavorid=4), + 'm1.xlarge': dict(memory_mb=16384, vcpus=8, local_gb=160, flavorid=5)} + try: + i = instance_types.insert() + for name, values in INSTANCE_TYPES.iteritems(): + # FIXME(kpepple) should we be seeding created_at / updated_at ? + # now = datetime.datatime.utcnow() + i.execute({'name': name, 'memory_mb': values["memory_mb"], + 'vcpus': values["vcpus"], 'deleted': 0, + 'local_gb': values["local_gb"], + 'flavorid': values["flavorid"]}) + except Exception: + logging.info(repr(table)) + logging.exception('Exception while seeding instance_types table') + raise + + +def downgrade(migrate_engine): + # Operations to reverse the above upgrade go here. + for table in (instance_types): + table.drop() -- cgit From f9cce74cad854d377de113a619dc42df10b9a2ba Mon Sep 17 00:00:00 2001 From: Masanori Itoh Date: Thu, 3 Mar 2011 14:10:42 +0900 Subject: Updated Authors and .mailmap --- .mailmap | 1 + Authors | 1 + 2 files changed, 2 insertions(+) diff --git a/.mailmap b/.mailmap index df012e066..ed4404ad5 100644 --- a/.mailmap +++ b/.mailmap @@ -15,6 +15,7 @@ + diff --git a/Authors b/Authors index b279d8a53..7993955e2 100644 --- a/Authors +++ b/Authors @@ -39,6 +39,7 @@ Ken Pepple Kevin L. Mitchell Koji Iida Lorin Hochstein +Masanori Itoh Matt Dietz Michael Gundlach Monsyne Dragon -- cgit From f7645bc2fb46ab7649faf12c794834d94839e4d2 Mon Sep 17 00:00:00 2001 From: Thierry Carrez Date: Thu, 3 Mar 2011 13:55:01 +0100 Subject: Fix regression in the way libvirt_conn gets its instance_types --- nova/virt/libvirt_conn.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py index 8a83e657f..9f7315c17 100644 --- a/nova/virt/libvirt_conn.py +++ b/nova/virt/libvirt_conn.py @@ -607,7 +607,7 @@ class LibvirtConnection(object): user=user, project=project, size=size) - type_data = instance_types.get_instance_type([inst['instance_type']]) + type_data = instance_types.get_instance_type(inst['instance_type']) if type_data['local_gb']: self._cache_image(fn=self._create_local, -- cgit From 26c217d1f16b100b9dc615388ee315e6daf336ce Mon Sep 17 00:00:00 2001 From: Masanori Itoh Date: Fri, 4 Mar 2011 00:30:19 +0900 Subject: Updated DescribeKeyPairs response tag checked in nova/tests/test_cloud.py --- nova/tests/test_cloud.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nova/tests/test_cloud.py b/nova/tests/test_cloud.py index 061910013..b195fa520 100644 --- a/nova/tests/test_cloud.py +++ b/nova/tests/test_cloud.py @@ -267,7 +267,7 @@ class CloudTestCase(test.TestCase): self._create_key('test1') self._create_key('test2') result = self.cloud.describe_key_pairs(self.context) - keys = result["keypairsSet"] + keys = result["keySet"] self.assertTrue(filter(lambda k: k['keyName'] == 'test1', keys)) self.assertTrue(filter(lambda k: k['keyName'] == 'test2', keys)) -- cgit From c363c2aaacb01cbbe8dcdaa4bda2e5d2531ab8e8 Mon Sep 17 00:00:00 2001 From: Rick Harris Date: Thu, 3 Mar 2011 18:21:54 +0000 Subject: Use %s in case instance_id came through as a string --- nova/compute/api.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/nova/compute/api.py b/nova/compute/api.py index 35a7d7bc0..5f68fcc4d 100644 --- a/nova/compute/api.py +++ b/nova/compute/api.py @@ -319,12 +319,12 @@ class API(base.Base): try: instance = self.get(context, instance_id) except exception.NotFound: - LOG.warning(_("Instance %d was not found during terminate"), + LOG.warning(_("Instance %s was not found during terminate"), instance_id) raise if (instance['state_description'] == 'terminating'): - LOG.warning(_("Instance %d is already being terminated"), + LOG.warning(_("Instance %s is already being terminated"), instance_id) return -- cgit From 0e1a458166ad1e89ca0755d88b8efec39855ee5c Mon Sep 17 00:00:00 2001 From: Cerberus Date: Thu, 3 Mar 2011 13:18:37 -0600 Subject: Renaming my migration yet again --- .../versions/007_add_instance_migrations.py | 61 ---------------------- .../versions/009_add_instance_migrations.py | 61 ++++++++++++++++++++++ 2 files changed, 61 insertions(+), 61 deletions(-) delete mode 100644 nova/db/sqlalchemy/migrate_repo/versions/007_add_instance_migrations.py create mode 100644 nova/db/sqlalchemy/migrate_repo/versions/009_add_instance_migrations.py diff --git a/nova/db/sqlalchemy/migrate_repo/versions/007_add_instance_migrations.py b/nova/db/sqlalchemy/migrate_repo/versions/007_add_instance_migrations.py deleted file mode 100644 index 4fda525f1..000000000 --- a/nova/db/sqlalchemy/migrate_repo/versions/007_add_instance_migrations.py +++ /dev/null @@ -1,61 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright 2010 OpenStack LLC. -# 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.from sqlalchemy import * - -from sqlalchemy import * -from migrate import * - -from nova import log as logging - - -meta = MetaData() - -# Just for the ForeignKey and column creation to succeed, these are not the -# actual definitions of instances or services. -instances = Table('instances', meta, - Column('id', Integer(), primary_key=True, nullable=False), - ) - -# -# New Tables -# - -migrations = Table('migrations', meta, - Column('created_at', DateTime(timezone=False)), - Column('updated_at', DateTime(timezone=False)), - Column('deleted_at', DateTime(timezone=False)), - Column('deleted', Boolean(create_constraint=True, name=None)), - Column('id', Integer(), primary_key=True, nullable=False), - Column('source_compute', String(255)), - Column('dest_compute', String(255)), - Column('dest_host', String(255)), - Column('instance_id', Integer, ForeignKey('instances.id'), - nullable=True), - Column('status', String(255)), - ) - - -def upgrade(migrate_engine): - # Upgrade operations go here. Don't create your own engine; - # bind migrate_engine to your metadata - meta.bind = migrate_engine - for table in (migrations, ): - try: - table.create() - except Exception: - logging.info(repr(table)) - logging.exception('Exception while creating table') - raise diff --git a/nova/db/sqlalchemy/migrate_repo/versions/009_add_instance_migrations.py b/nova/db/sqlalchemy/migrate_repo/versions/009_add_instance_migrations.py new file mode 100644 index 000000000..4fda525f1 --- /dev/null +++ b/nova/db/sqlalchemy/migrate_repo/versions/009_add_instance_migrations.py @@ -0,0 +1,61 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright 2010 OpenStack LLC. +# 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.from sqlalchemy import * + +from sqlalchemy import * +from migrate import * + +from nova import log as logging + + +meta = MetaData() + +# Just for the ForeignKey and column creation to succeed, these are not the +# actual definitions of instances or services. +instances = Table('instances', meta, + Column('id', Integer(), primary_key=True, nullable=False), + ) + +# +# New Tables +# + +migrations = Table('migrations', meta, + Column('created_at', DateTime(timezone=False)), + Column('updated_at', DateTime(timezone=False)), + Column('deleted_at', DateTime(timezone=False)), + Column('deleted', Boolean(create_constraint=True, name=None)), + Column('id', Integer(), primary_key=True, nullable=False), + Column('source_compute', String(255)), + Column('dest_compute', String(255)), + Column('dest_host', String(255)), + Column('instance_id', Integer, ForeignKey('instances.id'), + nullable=True), + Column('status', String(255)), + ) + + +def upgrade(migrate_engine): + # Upgrade operations go here. Don't create your own engine; + # bind migrate_engine to your metadata + meta.bind = migrate_engine + for table in (migrations, ): + try: + table.create() + except Exception: + logging.info(repr(table)) + logging.exception('Exception while creating table') + raise -- cgit From 1d8914fc752f7182f942cdd40f2ba18baedeed0c Mon Sep 17 00:00:00 2001 From: Cerberus Date: Fri, 4 Mar 2011 11:19:35 -0600 Subject: More fixes --- nova/api/openstack/servers.py | 4 ++-- nova/compute/api.py | 8 ++++---- nova/compute/manager.py | 2 +- nova/tests/xenapi/stubs.py | 2 +- nova/virt/xenapi/vm_utils.py | 16 ++++++++-------- nova/virt/xenapi/vmops.py | 11 ++++++----- 6 files changed, 22 insertions(+), 21 deletions(-) diff --git a/nova/api/openstack/servers.py b/nova/api/openstack/servers.py index ceb17c9e4..c2bf42b72 100644 --- a/nova/api/openstack/servers.py +++ b/nova/api/openstack/servers.py @@ -203,8 +203,8 @@ class Controller(wsgi.Controller): return exc.HTTPNoContent() def action(self, req, id): - """ Multi-purpose method used to reboot, rebuild, or - resize a server """ + """Multi-purpose method used to reboot, rebuild, or + resize a server""" actions = { 'reboot': self._action_reboot, diff --git a/nova/compute/api.py b/nova/compute/api.py index bfa5c7dba..ce9da727d 100644 --- a/nova/compute/api.py +++ b/nova/compute/api.py @@ -430,8 +430,8 @@ class API(base.Base): migration_ref = self.db.migration_get_by_instance_and_status(context, instance_id, 'finished') if not migration_ref: - raise exception.NotFound(_("No finished migrations found for \ - instance")) + raise exception.NotFound(_("No finished migrations found for " + "instance")) params = {'migration_id': migration_ref['id']} self._cast_compute_message('revert_resize', context, instance_id, @@ -444,8 +444,8 @@ class API(base.Base): migration_ref = self.db.migration_get_by_instance_and_status(context, instance_id, 'finished') if not migration_ref: - raise exception.NotFound(_("No finished migrations found for \ - instance")) + raise exception.NotFound(_("No finished migrations found for " + "instance")) instance_ref = self.db.instance_get(context, instance_id) params = {'migration_id': migration_ref['id']} self._cast_compute_message('confirm_resize', context, instance_id, diff --git a/nova/compute/manager.py b/nova/compute/manager.py index 1c42b383c..b3e864154 100644 --- a/nova/compute/manager.py +++ b/nova/compute/manager.py @@ -414,7 +414,7 @@ class ComputeManager(manager.Manager): @exception.wrap_exception @checks_instance_lock def confirm_resize(self, context, instance_id, migration_id): - """ Destroys the source instance """ + """Destroys the source instance""" context = context.elevated() instance_ref = self.db.instance_get(context, instance_id) migration_ref = self.db.migration_get(context, migration_id) diff --git a/nova/tests/xenapi/stubs.py b/nova/tests/xenapi/stubs.py index d17951b81..caefcff34 100644 --- a/nova/tests/xenapi/stubs.py +++ b/nova/tests/xenapi/stubs.py @@ -221,7 +221,7 @@ class FakeSessionForVolumeFailedTests(FakeSessionForVolumeTests): class FakeSessionForMigrationTests(fake.SessionBase): - """ Stubs out a XenAPISession for Migration tests """ + """Stubs out a XenAPISession for Migration tests""" def __init__(self, uri): super(FakeSessionForMigrationTests, self).__init__(uri) diff --git a/nova/virt/xenapi/vm_utils.py b/nova/virt/xenapi/vm_utils.py index ca2d634f1..eff207a51 100644 --- a/nova/virt/xenapi/vm_utils.py +++ b/nova/virt/xenapi/vm_utils.py @@ -270,8 +270,8 @@ class VMHelper(HelperBase): @classmethod def create_snapshot(cls, session, instance_id, vm_ref, label): - """ Creates Snapshot (Template) VM, Snapshot VBD, Snapshot VDI, - Snapshot VHD """ + """Creates Snapshot (Template) VM, Snapshot VBD, Snapshot VDI, + Snapshot VHD""" #TODO(sirp): Add quiesce and VSS locking support when Windows support # is added LOG.debug(_("Snapshotting VM %(vm_ref)s with label '%(label)s'...") @@ -284,7 +284,7 @@ class VMHelper(HelperBase): original_parent_uuid = get_vhd_parent_uuid(session, vm_vdi_ref) task = session.call_xenapi('Async.VM.snapshot', vm_ref, label) - template_vm_ref = session.wait_for_task(instance_id, task) + template_vm_ref = session.wait_for_task(task, instance_id) template_vdi_rec = cls.get_vdi_for_vm_safely(session, template_vm_ref)[1] template_vdi_uuid = template_vdi_rec["uuid"] @@ -302,14 +302,14 @@ class VMHelper(HelperBase): @classmethod def get_sr(cls, session, sr_label='slices'): - """ Finds the SR named by the given name label and returns - the UUID """ + """Finds the SR named by the given name label and returns + the UUID""" return session.call_xenapi('SR.get_by_name_label', sr_label)[0] @classmethod def get_sr_path(cls, session, sr_label='slices'): - """ Finds the SR and then coerces it into a path on the dom0 file - system """ + """Finds the SR and then coerces it into a path on the dom0 file + system""" return FLAGS.xenapi_sr_base_path + cls.get_sr(session, sr_label) @classmethod @@ -643,7 +643,7 @@ class VMHelper(HelperBase): if sr_ref: LOG.debug(_("Re-scanning SR %s"), sr_ref) task = session.call_xenapi('Async.SR.scan', sr_ref) - session.wait_for_task(instance_id, task) + session.wait_for_task(task, instance_id) @classmethod def scan_default_sr(cls, session): diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py index 60ce51f4a..01bfa2dc5 100644 --- a/nova/virt/xenapi/vmops.py +++ b/nova/virt/xenapi/vmops.py @@ -233,7 +233,7 @@ class VMOps(object): "start") def snapshot(self, instance, image_id): - """ Create snapshot from a running VM instance + """Create snapshot from a running VM instance :param instance: instance to be snapshotted :param image_id: id of image to upload to @@ -285,7 +285,7 @@ class VMOps(object): return def migrate_disk_and_power_off(self, instance, dest): - """ Copies a VHD from one host machine to another + """Copies a VHD from one host machine to another :param instance: the instance that owns the VHD in question :param dest: the destination host machine @@ -314,7 +314,7 @@ class VMOps(object): task = self._session.async_call_plugin('migration', 'transfer_vhd', {'params': pickle.dumps(params)}) - self._session.wait_for_task(instance.id, task) + self._session.wait_for_task(task, instance.id) # Now power down the instance and transfer the COW VHD self._shutdown(instance, vm_ref, method='clean') @@ -326,7 +326,7 @@ class VMOps(object): task = self._session.async_call_plugin('migration', 'transfer_vhd', {'params': pickle.dumps(params)}) - self._session.wait_for_task(instance.id, task) + self._session.wait_for_task(task, instance.id) finally: if template_vm_ref: @@ -338,6 +338,7 @@ class VMOps(object): return {'base_copy': base_copy_uuid, 'cow': cow_uuid} def attach_disk(self, instance, disk_info): + """Links the base copy VHD to the COW via the XAPI plugin""" vm_ref = VMHelper.lookup(self._session, instance.name) new_base_copy_uuid = str(uuid.uuid4()) new_cow_uuid = str(uuid.uuid4()) @@ -350,7 +351,7 @@ class VMOps(object): task = self._session.async_call_plugin('migration', 'move_vhds_into_sr', {'params': pickle.dumps(params)}) - self._session.wait_for_task(instance.id, task) + self._session.wait_for_task(task, instance.id) # Now we rescan the SR so we find the VHDs VMHelper.scan_default_sr(self._session) -- cgit From 831f398653cc99253bfeeb232165d3f9c043bd0b Mon Sep 17 00:00:00 2001 From: Todd Willey Date: Fri, 4 Mar 2011 14:01:25 -0500 Subject: Fix renaming of instance fields using update_instance method. --- nova/api/ec2/apirequest.py | 18 +++++++++++++++++- nova/api/ec2/cloud.py | 4 ++-- 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/nova/api/ec2/apirequest.py b/nova/api/ec2/apirequest.py index 2b1acba5a..d7ad08d2f 100644 --- a/nova/api/ec2/apirequest.py +++ b/nova/api/ec2/apirequest.py @@ -52,7 +52,23 @@ def _database_to_isoformat(datetimeobj): def _try_convert(value): - """Return a non-string if possible""" + """Return a non-string from a string or unicode, if possible. + + ============= ===================================================== + When value is returns + ============= ===================================================== + zero-length '' + 'None' None + 'True' True + 'False' False + '0', '-0' 0 + 0xN, -0xN int from hex (postitive) (N is any number) + 0bN, -0bN int from binary (positive) (N is any number) + * try conversion to int, float, complex, fallback value + + """ + if len(value) == 0: + return '' if value == 'None': return None if value == 'True': diff --git a/nova/api/ec2/cloud.py b/nova/api/ec2/cloud.py index c6309f03c..0d22a3f46 100644 --- a/nova/api/ec2/cloud.py +++ b/nova/api/ec2/cloud.py @@ -838,14 +838,14 @@ class CloudController(object): self.compute_api.unrescue(context, instance_id=instance_id) return True - def update_instance(self, context, ec2_id, **kwargs): + def update_instance(self, context, instance_id, **kwargs): updatable_fields = ['display_name', 'display_description'] changes = {} for field in updatable_fields: if field in kwargs: changes[field] = kwargs[field] if changes: - instance_id = ec2_id_to_id(ec2_id) + instance_id = ec2_id_to_id(instance_id) self.compute_api.update(context, instance_id=instance_id, **kwargs) return True -- cgit From a775c4eee279e11268a6cc447aee24c452e4665a Mon Sep 17 00:00:00 2001 From: "matt.dietz@rackspace.com" <> Date: Mon, 7 Mar 2011 17:17:41 +0000 Subject: Merge prop changes and test fixes --- nova/tests/xenapi/stubs.py | 26 +++++++++++++------------- nova/virt/xenapi/vm_utils.py | 30 ++++++++++++------------------ nova/virt/xenapi/vmops.py | 4 ++-- 3 files changed, 27 insertions(+), 33 deletions(-) diff --git a/nova/tests/xenapi/stubs.py b/nova/tests/xenapi/stubs.py index caefcff34..11e89c9b4 100644 --- a/nova/tests/xenapi/stubs.py +++ b/nova/tests/xenapi/stubs.py @@ -227,18 +227,8 @@ class FakeSessionForMigrationTests(fake.SessionBase): def stub_out_migration_methods(stubs): - class FakeSnapshot(object): - def __getattr__(self, key): - return str(key) - - def __enter__(self): - return self - - def __exit__(self, type, value, traceback): - pass - def fake_get_snapshot(self, instance): - return FakeSnapshot() + return 'foo', 'bar' @classmethod def fake_get_vdi(cls, session, vm_ref): @@ -251,11 +241,21 @@ def stub_out_migration_methods(stubs): pass @classmethod - def fake_scan_sr(cls, session): + def fake_sr(cls, session, *args): + pass + + @classmethod + def fake_get_sr_path(cls, *args): + return "fake" + + def fake_destroy(*args, **kwargs): pass - stubs.Set(vm_utils.VMHelper, 'scan_sr', fake_scan_sr) + stubs.Set(vmops.VMOps, '_destroy', fake_destroy) + stubs.Set(vm_utils.VMHelper, 'scan_default_sr', fake_sr) + stubs.Set(vm_utils.VMHelper, 'scan_sr', fake_sr) stubs.Set(vmops.VMOps, '_get_snapshot', fake_get_snapshot) stubs.Set(vm_utils.VMHelper, 'get_vdi_for_vm_safely', fake_get_vdi) stubs.Set(xenapi_conn.XenAPISession, 'wait_for_task', lambda x, y, z: None) + stubs.Set(vm_utils.VMHelper, 'get_sr_path', fake_get_sr_path) stubs.Set(vmops.VMOps, '_shutdown', fake_shutdown) diff --git a/nova/virt/xenapi/vm_utils.py b/nova/virt/xenapi/vm_utils.py index eff207a51..80b7540d4 100644 --- a/nova/virt/xenapi/vm_utils.py +++ b/nova/virt/xenapi/vm_utils.py @@ -307,10 +307,16 @@ class VMHelper(HelperBase): return session.call_xenapi('SR.get_by_name_label', sr_label)[0] @classmethod - def get_sr_path(cls, session, sr_label='slices'): - """Finds the SR and then coerces it into a path on the dom0 file - system""" - return FLAGS.xenapi_sr_base_path + cls.get_sr(session, sr_label) + def get_sr_path(cls, session): + """Return the path to our storage repository + + This is used when we're dealing with VHDs directly, either by taking + snapshots or by restoring an image in the DISK_VHD format. + """ + sr_ref = safe_find_sr(session) + sr_rec = session.get_xenapi().SR.get_record(sr_ref) + sr_uuid = sr_rec["uuid"] + return os.path.join(FLAGS.xenapi_sr_base_path, sr_uuid) @classmethod def upload_image(cls, session, instance_id, vdi_uuids, image_id): @@ -326,7 +332,7 @@ class VMHelper(HelperBase): 'image_id': image_id, 'glance_host': FLAGS.glance_host, 'glance_port': FLAGS.glance_port, - 'sr_path': get_sr_path(session)} + 'sr_path': cls.get_sr_path(session)} kwargs = {'params': pickle.dumps(params)} task = session.async_call_plugin('glance', 'upload_vhd', kwargs) @@ -369,7 +375,7 @@ class VMHelper(HelperBase): 'glance_host': FLAGS.glance_host, 'glance_port': FLAGS.glance_port, 'uuid_stack': uuid_stack, - 'sr_path': get_sr_path(session)} + 'sr_path': cls.get_sr_path(session)} kwargs = {'params': pickle.dumps(params)} task = session.async_call_plugin('glance', 'download_vhd', kwargs) @@ -775,18 +781,6 @@ def find_sr(session): return None -def get_sr_path(session): - """Return the path to our storage repository - - This is used when we're dealing with VHDs directly, either by taking - snapshots or by restoring an image in the DISK_VHD format. - """ - sr_ref = safe_find_sr(session) - sr_rec = session.get_xenapi().SR.get_record(sr_ref) - sr_uuid = sr_rec["uuid"] - return os.path.join(FLAGS.xenapi_sr_base_path, sr_uuid) - - def remap_vbd_dev(dev): """Return the appropriate location for a plugged-in VBD device diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py index 01bfa2dc5..b862c9de9 100644 --- a/nova/virt/xenapi/vmops.py +++ b/nova/virt/xenapi/vmops.py @@ -262,7 +262,7 @@ class VMOps(object): self._session, instance.id, template_vdi_uuids, image_id) finally: if template_vm_ref: - self.virt._destroy(self.instance, template_vm_ref, + self._destroy(instance, template_vm_ref, shutdown=False, destroy_kernel_ramdisk=False) logging.debug(_("Finished snapshot and upload for VM %s"), instance) @@ -330,7 +330,7 @@ class VMOps(object): finally: if template_vm_ref: - self.virt._destroy(self.instance, template_vm_ref, + self._destroy(instance, template_vm_ref, shutdown=False, destroy_kernel_ramdisk=False) # TODO(mdietz): we could also consider renaming these to something -- cgit From f72366f007239656d3d5e3fc80cd277758eedf9b Mon Sep 17 00:00:00 2001 From: "Kevin L. Mitchell" Date: Mon, 7 Mar 2011 19:33:24 +0000 Subject: Create --paste_config flag defaulting to api-paste.ini and mv etc/nova-api.conf to match --- bin/nova-api | 7 +++-- etc/api-paste.ini | 91 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ etc/nova-api.conf | 91 ------------------------------------------------------- 3 files changed, 96 insertions(+), 93 deletions(-) create mode 100644 etc/api-paste.ini delete mode 100644 etc/nova-api.conf diff --git a/bin/nova-api b/bin/nova-api index 14be4b841..0b2a44c88 100755 --- a/bin/nova-api +++ b/bin/nova-api @@ -43,6 +43,8 @@ from nova import wsgi LOG = logging.getLogger('nova.api') FLAGS = flags.FLAGS +flags.DEFINE_string('paste_config', "api-paste.ini", + 'File name for the paste.deploy config for nova-api') flags.DEFINE_string('ec2_listen', "0.0.0.0", 'IP address for EC2 API to listen') flags.DEFINE_integer('ec2_listen_port', 8773, 'port for ec2 api to listen') @@ -90,8 +92,9 @@ if __name__ == '__main__': for flag in FLAGS: flag_get = FLAGS.get(flag, None) LOG.debug("%(flag)s : %(flag_get)s" % locals()) - conf = wsgi.paste_config_file('nova-api.conf') + conf = wsgi.paste_config_file(FLAGS.paste_config) if conf: run_app(conf) else: - LOG.error(_("No paste configuration found for: %s"), 'nova-api.conf') + LOG.error(_("No paste configuration found for: %s"), + FLAGS.paste_config) diff --git a/etc/api-paste.ini b/etc/api-paste.ini new file mode 100644 index 000000000..9f7e93d4c --- /dev/null +++ b/etc/api-paste.ini @@ -0,0 +1,91 @@ +####### +# EC2 # +####### + +[composite:ec2] +use = egg:Paste#urlmap +/: ec2versions +/services/Cloud: ec2cloud +/services/Admin: ec2admin +/latest: ec2metadata +/2007-01-19: ec2metadata +/2007-03-01: ec2metadata +/2007-08-29: ec2metadata +/2007-10-10: ec2metadata +/2007-12-15: ec2metadata +/2008-02-01: ec2metadata +/2008-09-01: ec2metadata +/2009-04-04: ec2metadata +/1.0: ec2metadata + +[pipeline:ec2cloud] +pipeline = logrequest authenticate cloudrequest authorizer ec2executor +#pipeline = logrequest ec2lockout authenticate cloudrequest authorizer ec2executor + +[pipeline:ec2admin] +pipeline = logrequest authenticate adminrequest authorizer ec2executor + +[pipeline:ec2metadata] +pipeline = logrequest ec2md + +[pipeline:ec2versions] +pipeline = logrequest ec2ver + +[filter:logrequest] +paste.filter_factory = nova.api.ec2:RequestLogging.factory + +[filter:ec2lockout] +paste.filter_factory = nova.api.ec2:Lockout.factory + +[filter:authenticate] +paste.filter_factory = nova.api.ec2:Authenticate.factory + +[filter:cloudrequest] +controller = nova.api.ec2.cloud.CloudController +paste.filter_factory = nova.api.ec2:Requestify.factory + +[filter:adminrequest] +controller = nova.api.ec2.admin.AdminController +paste.filter_factory = nova.api.ec2:Requestify.factory + +[filter:authorizer] +paste.filter_factory = nova.api.ec2:Authorizer.factory + +[app:ec2executor] +paste.app_factory = nova.api.ec2:Executor.factory + +[app:ec2ver] +paste.app_factory = nova.api.ec2:Versions.factory + +[app:ec2md] +paste.app_factory = nova.api.ec2.metadatarequesthandler:MetadataRequestHandler.factory + +############# +# Openstack # +############# + +[composite:osapi] +use = egg:Paste#urlmap +/: osversions +/v1.0: openstackapi + +[pipeline:openstackapi] +pipeline = faultwrap auth ratelimit osapiapp + +[filter:faultwrap] +paste.filter_factory = nova.api.openstack:FaultWrapper.factory + +[filter:auth] +paste.filter_factory = nova.api.openstack.auth:AuthMiddleware.factory + +[filter:ratelimit] +paste.filter_factory = nova.api.openstack.ratelimiting:RateLimitingMiddleware.factory + +[app:osapiapp] +paste.app_factory = nova.api.openstack:APIRouter.factory + +[pipeline:osversions] +pipeline = faultwrap osversionapp + +[app:osversionapp] +paste.app_factory = nova.api.openstack:Versions.factory diff --git a/etc/nova-api.conf b/etc/nova-api.conf deleted file mode 100644 index 9f7e93d4c..000000000 --- a/etc/nova-api.conf +++ /dev/null @@ -1,91 +0,0 @@ -####### -# EC2 # -####### - -[composite:ec2] -use = egg:Paste#urlmap -/: ec2versions -/services/Cloud: ec2cloud -/services/Admin: ec2admin -/latest: ec2metadata -/2007-01-19: ec2metadata -/2007-03-01: ec2metadata -/2007-08-29: ec2metadata -/2007-10-10: ec2metadata -/2007-12-15: ec2metadata -/2008-02-01: ec2metadata -/2008-09-01: ec2metadata -/2009-04-04: ec2metadata -/1.0: ec2metadata - -[pipeline:ec2cloud] -pipeline = logrequest authenticate cloudrequest authorizer ec2executor -#pipeline = logrequest ec2lockout authenticate cloudrequest authorizer ec2executor - -[pipeline:ec2admin] -pipeline = logrequest authenticate adminrequest authorizer ec2executor - -[pipeline:ec2metadata] -pipeline = logrequest ec2md - -[pipeline:ec2versions] -pipeline = logrequest ec2ver - -[filter:logrequest] -paste.filter_factory = nova.api.ec2:RequestLogging.factory - -[filter:ec2lockout] -paste.filter_factory = nova.api.ec2:Lockout.factory - -[filter:authenticate] -paste.filter_factory = nova.api.ec2:Authenticate.factory - -[filter:cloudrequest] -controller = nova.api.ec2.cloud.CloudController -paste.filter_factory = nova.api.ec2:Requestify.factory - -[filter:adminrequest] -controller = nova.api.ec2.admin.AdminController -paste.filter_factory = nova.api.ec2:Requestify.factory - -[filter:authorizer] -paste.filter_factory = nova.api.ec2:Authorizer.factory - -[app:ec2executor] -paste.app_factory = nova.api.ec2:Executor.factory - -[app:ec2ver] -paste.app_factory = nova.api.ec2:Versions.factory - -[app:ec2md] -paste.app_factory = nova.api.ec2.metadatarequesthandler:MetadataRequestHandler.factory - -############# -# Openstack # -############# - -[composite:osapi] -use = egg:Paste#urlmap -/: osversions -/v1.0: openstackapi - -[pipeline:openstackapi] -pipeline = faultwrap auth ratelimit osapiapp - -[filter:faultwrap] -paste.filter_factory = nova.api.openstack:FaultWrapper.factory - -[filter:auth] -paste.filter_factory = nova.api.openstack.auth:AuthMiddleware.factory - -[filter:ratelimit] -paste.filter_factory = nova.api.openstack.ratelimiting:RateLimitingMiddleware.factory - -[app:osapiapp] -paste.app_factory = nova.api.openstack:APIRouter.factory - -[pipeline:osversions] -pipeline = faultwrap osversionapp - -[app:osversionapp] -paste.app_factory = nova.api.openstack:Versions.factory -- cgit