diff --git a/gns3server/modules/base.py b/gns3server/modules/base.py index b62213af..f9c19ca0 100644 --- a/gns3server/modules/base.py +++ b/gns3server/modules/base.py @@ -159,7 +159,7 @@ class IModule(multiprocessing.Process): if signum: self._ioloop.add_callback_from_signal(self._shutdown) else: - self._shutdown() + self._ioloop.add_callback(self._shutdown) def send_response(self, results): """ @@ -240,6 +240,13 @@ class IModule(multiprocessing.Process): if self._stopping: return + # handle special request to stop the module + # e.g. useful on Windows where the + # SIGBREAK signal cound't be propagated + if request[0] == b"stop": + self.stop() + return + try: request = zmq.utils.jsonapi.loads(request[0]) except ValueError: diff --git a/gns3server/modules/dynamips/__init__.py b/gns3server/modules/dynamips/__init__.py index 62ea82c6..9cc3c752 100644 --- a/gns3server/modules/dynamips/__init__.py +++ b/gns3server/modules/dynamips/__init__.py @@ -132,9 +132,8 @@ class Dynamips(IModule): if not sys.platform.startswith("win32"): self._callback.stop() - if self._hypervisor_manager: - self._hypervisor_manager.stop_all_hypervisors() + self.reset() IModule.stop(self, signum) # this will stop the I/O loop def _check_hypervisors(self): @@ -175,11 +174,11 @@ class Dynamips(IModule): return instance_dict[device_id] @IModule.route("dynamips.reset") - def reset(self, request): + def reset(self, request=None): """ - Resets the module. + Resets the module (JSON-RPC notification). - :param request: JSON request + :param request: JSON request (not used) """ # stop all Dynamips hypervisors diff --git a/gns3server/modules/iou/__init__.py b/gns3server/modules/iou/__init__.py index bf13e665..47b0912f 100644 --- a/gns3server/modules/iou/__init__.py +++ b/gns3server/modules/iou/__init__.py @@ -112,11 +112,7 @@ class IOU(IModule): """ self._iou_callback.stop() - # delete all IOU instances - for iou_id in self._iou_instances: - iou_instance = self._iou_instances[iou_id] - iou_instance.delete() - + self.reset() IModule.stop(self, signum) # this will stop the I/O loop def _check_iou_is_alive(self): @@ -161,11 +157,11 @@ class IOU(IModule): return self._iou_instances[iou_id] @IModule.route("iou.reset") - def reset(self, request): + def reset(self, request=None): """ - Resets the module. + Resets the module (JSON-RPC notification). - :param request: JSON request + :param request: JSON request (not used) """ # delete all IOU instances diff --git a/gns3server/server.py b/gns3server/server.py index 5452ac23..c7237c91 100644 --- a/gns3server/server.py +++ b/gns3server/server.py @@ -215,6 +215,17 @@ class Server(object): log.info("ZeroMQ server listening to 127.0.0.1:{}".format(self._zmq_port)) return self._router + def stop_module(self, module): + """ + Stop a given module. + + :param module: module name + """ + + if not self._router.closed: + self._router.send_string(module, zmq.SNDMORE) + self._router.send_string("stop") + def _shutdown(self): """ Shutdowns the I/O loop and the ZeroMQ stream & socket. @@ -243,13 +254,18 @@ class Server(object): # terminate all modules for module in self._modules: if module.is_alive(): - log.info("terminating {}".format(module.name)) - module.terminate() - module.join(timeout=1) + log.info("stopping {}".format(module.name)) + self.stop_module(module.name) + module.join(timeout=3) + if module.is_alive(): + # just kill the module if it is still alive. + log.info("terminating {}".format(module.name)) + module.terminate() + module.join(timeout=1) if stop: ioloop = tornado.ioloop.IOLoop.instance() if signum: ioloop.add_callback_from_signal(self._shutdown) else: - self._shutdown() + ioloop.add_callback(self._shutdown)