X-Git-Url: https://osm.etsi.org/gitweb/?a=blobdiff_plain;f=rwcal%2Frift%2Fcal%2Fserver%2Foperations.py;fp=rwcal%2Frift%2Fcal%2Fserver%2Foperations.py;h=316525e91284bd13d8738880f936236638bbbd1a;hb=6f07e6f33f751ab4ffe624f6037f887b243bece2;hp=0000000000000000000000000000000000000000;hpb=72a563886272088feb7cb52e4aafbe6d2c580ff9;p=osm%2FSO.git diff --git a/rwcal/rift/cal/server/operations.py b/rwcal/rift/cal/server/operations.py new file mode 100644 index 00000000..316525e9 --- /dev/null +++ b/rwcal/rift/cal/server/operations.py @@ -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)