blob: 316525e91284bd13d8738880f936236638bbbd1a [file] [log] [blame]
"""
#
# 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)