--- /dev/null
+"""
+#
+# 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)