RIFT OSM R1 Initial Submission
[osm/SO.git] / rwcal / rift / cal / server / operations.py
diff --git a/rwcal/rift/cal/server/operations.py b/rwcal/rift/cal/server/operations.py
new file mode 100644 (file)
index 0000000..316525e
--- /dev/null
@@ -0,0 +1,200 @@
+"""
+# 
+#   Copyright 2016 RIFT.IO Inc
+#
+#   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.
+#
+
+@file operations.py
+@author Varun Prasad(varun.prasad@riftio.com)
+@date 2016-06-14
+"""
+
+import daemon
+import daemon.pidfile
+import os
+import signal
+import subprocess
+import sys
+import time
+
+import gi
+gi.require_version('RwcalYang', '1.0')
+gi.require_version('RwCal', '1.0')
+gi.require_version('RwLog', '1.0')
+
+from . import server as cal_server
+import rift.cal.utils as cal_util
+import rift.rwcal.cloudsim.shell as shell
+
+
+
+class CloudsimServerOperations(cal_util.CloudSimCalMixin):
+    """Convenience class to provide start, stop and cleanup operations
+    
+    Attributes:
+        log (logging): Log instance
+        PID_FILE (str): Location to generate the PID file.
+    """
+    PID_FILE = "/var/log/rift/cloudsim_server.pid"
+
+    def __init__(self, log):
+        super().__init__()
+        self.log = log
+
+    @property
+    def pid(self):
+        pid = None
+        try:
+            with open(self.PID_FILE) as fh:
+                pid = fh.readlines()[0]
+                pid = int(pid.strip())
+        except IndexError:
+            self.log.error("Looks like the pid file does not contain a valid ID")
+        except OSError:
+            self.log.debug("No PID file found.")
+
+        return pid
+
+    def is_pid_exists(self, pid):
+        try:
+            os.kill(pid, 0)
+        except OSError:
+            return False
+
+        return True
+
+    def start_server(self, foreground=False):
+        """Start the tornado app """
+
+        # Before starting verify if all requirements are satisfied
+        cal_server.CalServer.verify_requirements(self.log)
+
+        # If the /var/log directory is not present, then create it first.
+        if not os.path.exists(os.path.dirname(self.PID_FILE)):
+            self.log.warning("Creating /var/log/rift directory for log storage")
+            os.makedirs(os.path.dirname(self.PID_FILE))
+
+        # Check if an exiting PID file is present, if so check if it has an
+        # associated proc, otherwise it's a zombie file so clean it.
+        # Otherwise the daemon fails silently.
+        if self.pid is not None and not self.is_pid_exists(self.pid):
+            self.log.warning("Removing stale PID file")
+            os.remove(self.PID_FILE)
+
+
+
+        def start(daemon_mode=False):
+
+            log = cal_util.Logger(daemon_mode=daemon_mode, log_name='')
+            log.logger.info("Starting the cloud server.")
+            server = cal_server.CalServer()
+            server.start()
+
+        if foreground:
+            # Write the PID file for consistency
+            with open(self.PID_FILE, mode='w') as fh:
+                fh.write(str(os.getpid()) + "\n")
+            start()
+        else:
+            context = daemon.DaemonContext(
+                pidfile=daemon.pidfile.PIDLockFile(self.PID_FILE))
+            with context:
+                start(daemon_mode=True)
+
+    def stop_server(self):
+        """Stop the daemon"""
+
+        def kill_pid(pid, sig):
+            self.log.info("Sending {} to PID: {}".format(str(sig), pid))
+            os.kill(pid, sig)
+
+
+        def search_and_kill():
+            """In case the PID file is not found, and the server is still
+            running, as a last resort we search thro' the process table
+            and stop the server."""
+            cmd = ["pgrep", "-u", "daemon,root", "python3"]
+
+            try:
+               pids = subprocess.check_output(cmd)
+            except subprocess.CalledProcessError:
+                self.log.error("No Cloudsim server process found. "
+                        "Please ensure Cloudsim server is running")
+                return
+
+            pids = map(int, pids.split())
+
+            for pid in pids:
+                if pid != os.getpid():
+                    kill_sequence(pid)
+
+        def wait_till_exit(pid, timeout=30, retry_interval=1):
+            start_time = time.time()
+
+            while True:
+                if not self.is_pid_exists(pid):
+                    msg = "Killed {}".format(pid)
+                    print (msg)
+                    return True
+
+                time_elapsed = time.time() - start_time
+                time_remaining = timeout - time_elapsed
+
+                self.log.info("Process still exists, trying again in {} sec(s)"
+                    .format(retry_interval))
+
+                if time_remaining <= 0:
+                    msg = 'Process {} has not yet terminated within {} secs. Trying SIGKILL'
+                    self.log.error(msg.format(pid, timeout))
+                    return False
+
+                time.sleep(min(time_remaining, retry_interval))
+
+        def kill_sequence(pid):
+            kill_pid(pid, signal.SIGHUP)
+            wait_till_exit(pid, timeout=10, retry_interval=2)
+            kill_pid(pid, signal.SIGKILL)
+            status = wait_till_exit(pid)
+
+            if status:
+                # Remove the lock file.
+                shell.command("rm -f {}".format(self.PID_FILE))
+
+        pid = self.pid
+        if pid is not None:
+            self.log.warning("Server running with PID: {} found, "
+                             "trying to stop it".format(pid))
+            kill_sequence(pid)
+        else:
+            self.log.warning("No PID file found. Searching the process "
+                            "table to find PID")
+            search_and_kill()
+
+    def clean_server(self, images=False):
+        """Clean all resource using rest APIs. """
+
+        # Delete VDUs
+        _, vdus = self.cal.get_vdu_list(self.account)
+        for vdu in vdus.vdu_info_list:
+            self.cal.delete_vdu(self.account, vdu.vdu_id)
+
+        # Delete Vlinks
+        _, vlinks = self.cal.get_virtual_link_list(self.account)
+        for vlink in vlinks.virtual_link_info_list:
+            self.cal.delete_virtual_link(self.account, vlink.virtual_link_id)
+
+        if images:
+            _, images = self.cal.get_image_list(self.account)
+            for image in images.image_info_list:
+                self.cal.delete_image(self.account, image.id)