From bb000df3bf8dc4a456aad20868b2e081e1790124 Mon Sep 17 00:00:00 2001 From: Paul Marshall Date: Thu, 5 Jul 2012 14:51:55 -0500 Subject: [PATCH] adding mgmt action to reboot an instance --- etc/reddwarf/reddwarf.conf.sample | 3 ++ reddwarf/extensions/mgmt/instances/service.py | 38 +++++++++++++++++-- reddwarf/instance/models.py | 8 +++- reddwarf/taskmanager/api.py | 4 ++ reddwarf/taskmanager/manager.py | 4 ++ reddwarf/taskmanager/models.py | 33 +++++++++++++++- reddwarf/tests/fakes/nova.py | 19 ++++++++++ 7 files changed, 102 insertions(+), 7 deletions(-) diff --git a/etc/reddwarf/reddwarf.conf.sample b/etc/reddwarf/reddwarf.conf.sample index 5f87012466..704fd9a1e7 100644 --- a/etc/reddwarf/reddwarf.conf.sample +++ b/etc/reddwarf/reddwarf.conf.sample @@ -71,6 +71,9 @@ agent_heartbeat_time = 10 agent_call_low_timeout = 5 agent_call_high_timeout = 100 +# Reboot time out for instances +reboot_time_out = 60 + # ============ notifer queue kombu connection options ======================== notifier_queue_hostname = localhost diff --git a/reddwarf/extensions/mgmt/instances/service.py b/reddwarf/extensions/mgmt/instances/service.py index 8bc8402adc..da403f12a7 100644 --- a/reddwarf/extensions/mgmt/instances/service.py +++ b/reddwarf/extensions/mgmt/instances/service.py @@ -73,12 +73,42 @@ class MgmtInstanceController(InstanceController): @admin_context def action(self, req, body, tenant_id, id): - LOG.info("Request made by tenant %s to stop instance %s." - % (tenant_id, id)) + LOG.info("req : '%s'\n\n" % req) + LOG.info("Committing an ACTION against instance %s for tenant '%s'" + % (id, tenant_id)) + if not body: + raise exception.BadRequest(_("Invalid request body.")) context = req.environ[wsgi.CONTEXT_KEY] instance = models.MgmtInstance.load(context=context, id=id) - if 'stop' in body: - instance.stop_mysql() + _actions = { + 'stop': self._action_stop, + 'reboot': self._action_reboot + } + selected_action = None + for key in body: + if key in _actions: + if selected_action is not None: + msg = _("Only one action can be specified per request.") + raise exception.BadRequest(msg) + selected_action = _actions[key] + else: + msg = _("Invalid instance action: %s") % key + raise exception.BadRequest(msg) + + if selected_action: + return selected_action(context, instance, body) + else: + raise exception.BadRequest(_("Invalid request body.")) + + def _action_stop(self, context, instance, body): + LOG.debug("Stopping MySQL on instance %s." % instance.id) + instance.stop_mysql() + return wsgi.Result(None, 202) + + def _action_reboot(self, context, instance, body): + LOG.debug("Rebooting instance %s." % instance.id) + instance.reboot() + return wsgi.Result(None, 202) @admin_context def root(self, req, tenant_id, id): diff --git a/reddwarf/instance/models.py b/reddwarf/instance/models.py index 0dc7375b1b..02b4cd6e33 100644 --- a/reddwarf/instance/models.py +++ b/reddwarf/instance/models.py @@ -461,9 +461,15 @@ class Instance(BuiltInstance): self.update_db(task_status=InstanceTasks.RESIZING) task_api.API(self.context).resize_volume(new_size, self.id) + def reboot(self): + self._validate_can_perform_action() + LOG.info("Rebooting instance %s..." % self.id) + self.update_db(task_status=InstanceTasks.REBOOTING) + task_api.API(self.context).reboot(self.id) + def restart(self): self._validate_can_perform_action() - LOG.info("Restarting instance %s..." % self.id) + LOG.info("Restarting MySQL on instance %s..." % self.id) # Set our local status since Nova might not change it quick enough. #TODO(tim.simpson): Possible bad stuff can happen if this service # shuts down before it can set status to NONE. diff --git a/reddwarf/taskmanager/api.py b/reddwarf/taskmanager/api.py index 7a4d6c4014..bc8126de83 100644 --- a/reddwarf/taskmanager/api.py +++ b/reddwarf/taskmanager/api.py @@ -69,6 +69,10 @@ class API(ManagerAPI): old_memory_size=old_memory_size, new_memory_size=new_memory_size) + def reboot(self, instance_id): + LOG.debug("Making async call to reboot instance: %s" % instance_id) + self._cast("reboot", instance_id=instance_id) + def restart(self, instance_id): LOG.debug("Making async call to restart instance: %s" % instance_id) self._cast("restart", instance_id=instance_id) diff --git a/reddwarf/taskmanager/manager.py b/reddwarf/taskmanager/manager.py index 2cb25bc366..fdc5e94e0e 100644 --- a/reddwarf/taskmanager/manager.py +++ b/reddwarf/taskmanager/manager.py @@ -49,6 +49,10 @@ class TaskManager(service.Manager): instance_tasks.resize_flavor(new_flavor_id, old_memory_size, new_memory_size) + def reboot(self, context, instance_id): + instance_tasks = models.BuiltInstanceTasks.load(context, instance_id) + instance_tasks.reboot() + def restart(self, context, instance_id): instance_tasks = models.BuiltInstanceTasks.load(context, instance_id) instance_tasks.restart() diff --git a/reddwarf/taskmanager/models.py b/reddwarf/taskmanager/models.py index fb66837283..dcb49961b1 100644 --- a/reddwarf/taskmanager/models.py +++ b/reddwarf/taskmanager/models.py @@ -400,11 +400,40 @@ class BuiltInstanceTasks(BuiltInstance): finally: self.update_db(task_status=inst_models.InstanceTasks.NONE) + def reboot(self): + try: + LOG.debug("Instance %s calling stop_mysql..." % self.id) + self.guest.stop_mysql() + LOG.debug("Rebooting instance %s" % self.id) + self.server.reboot() + + # Poll nova until instance is active + reboot_time_out = int(config.Config.get("reboot_time_out", 60 * 2)) + def update_server_info(): + self._refresh_compute_server_info() + return self.server.status == 'ACTIVE' + utils.poll_until( + update_server_info, + sleep_time=2, + time_out=reboot_time_out) + + # Set the status to PAUSED. The guest agent will reset the status + # when the reboot completes and MySQL is running. + status = InstanceServiceStatus.find_by(instance_id=self.id) + status.set_status(inst_models.ServiceStatuses.PAUSED) + status.save() + LOG.debug("Successfully rebooted instance %s" % self.id) + except Exception, e: + LOG.error("Failed to reboot instance %s: %s" % (self.id, str(e))) + finally: + LOG.debug("Rebooting FINALLY %s" % self.id) + self.update_db(task_status=inst_models.InstanceTasks.NONE) + def restart(self): - LOG.debug("Restarting instance %s " % self.id) + LOG.debug("Restarting MySQL on instance %s " % self.id) try: self.guest.restart() - LOG.debug("Restarting successful %s " % self.id) + LOG.debug("Restarting MySQL successful %s " % self.id) except GuestError: LOG.error("Failure to restart MySQL for instance %s." % self.id) finally: diff --git a/reddwarf/tests/fakes/nova.py b/reddwarf/tests/fakes/nova.py index 04bbf04711..0a1c76f016 100644 --- a/reddwarf/tests/fakes/nova.py +++ b/reddwarf/tests/fakes/nova.py @@ -113,6 +113,13 @@ class FakeServer(object): raise RuntimeError("Not in resize confirm mode.") self._current_status = "ACTIVE" + def reboot(self): + LOG.debug("Rebooting server %s" % (self.id)) + self._current_status = "REBOOT" + time.sleep(1) + self._current_status = "ACTIVE" + self.parent.schedule_simulate_running_server(self.id, 1.5) + def delete(self): self.schedule_status = [] # TODO(pdmars): This is less than ideal, but a quick way to force it @@ -253,6 +260,18 @@ class FakeServers(object): del self.db[id] self.events.add_event(time_from_now, delete_server) + def schedule_simulate_running_server(self, id, time_from_now): + def set_server_running(): + from reddwarf.instance.models import DBInstance + from reddwarf.instance.models import InstanceServiceStatus + from reddwarf.instance.models import ServiceStatuses + instance = DBInstance.find_by(compute_instance_id=id) + LOG.debug("Setting server %s to running" % instance.id) + status = InstanceServiceStatus.find_by(instance_id=instance.id) + status.status = ServiceStatuses.RUNNING + status.save() + self.events.add_event(time_from_now, set_server_running) + class FakeServerVolumes(object):