[virt-who] Improve command line arguments behaviour.
by Radek Novacek
commit ef9a4875f1e23c4d423726ed22252b0742555adf
Author: Radek Novacek <rnovacek(a)redhat.com>
Date: Thu Jun 14 10:50:42 2012 +0200
Improve command line arguments behaviour.
* Add -o (--one-shot) command line option
* Start on foreground and only print error messages by default
* Do double-fork and don't print anything with -b
* Always write debug messages to log file
log.py | 30 ++++++++++++++-----
subscriptionmanager.py | 47 ++++++++++++++++++++----------
virt-who.conf | 3 +-
virt-who.py | 75 ++++++++++++++++++++++++++++++-----------------
4 files changed, 103 insertions(+), 52 deletions(-)
---
diff --git a/log.py b/log.py
index f2fa0de..87fc94a 100644
--- a/log.py
+++ b/log.py
@@ -24,24 +24,38 @@ import logging.handlers
import os
import sys
-def init_logger():
- logging.getLogger("rhsm-app").addHandler(_get_handler())
+def getLogger(debug, background):
+ logger = logging.getLogger("rhsm-app")
-def _get_handler():
path = '/var/log/rhsm/rhsm.log'
try:
if not os.path.isdir("/var/log/rhsm"):
os.mkdir("/var/log/rhsm")
except:
pass
- fmt = '%(asctime)s [%(levelname)s] @%(filename)s:%(lineno)d - %(message)s'
# Try to write to /var/log, fallback on console logging:
try:
- handler = logging.handlers.RotatingFileHandler(path, maxBytes=0x100000, backupCount=5)
+ fileHandler = logging.handlers.RotatingFileHandler(path, maxBytes=0x100000, backupCount=5)
+ fileHandler.setFormatter(logging.Formatter('%(asctime)s [%(levelname)s] @%(filename)s:%(lineno)d - %(message)s'))
+ fileHandler.setLevel(logging.DEBUG)
+ logger.addHandler(fileHandler)
except Exception, e:
sys.stderr.write("Unable to log to %s: %s\n" % (path, e))
- handler = logging.StreamHandler()
- handler.setFormatter(logging.Formatter(fmt))
- return handler
+ if not background:
+ streamHandler = logging.StreamHandler()
+ streamHandler.setFormatter(logging.Formatter('%(levelname)s: %(message)s'))
+ if debug:
+ streamHandler.setLevel(logging.DEBUG)
+ else:
+ streamHandler.setLevel(logging.WARNING)
+
+ # Don't print exceptions to stdout in non-debug mode
+ f = logging.Filter()
+ f.filter = lambda record: record.exc_info is None
+ streamHandler.addFilter(f)
+
+ logger.addHandler(streamHandler)
+
+ return logger
diff --git a/subscriptionmanager.py b/subscriptionmanager.py
index cd7a8c3..1b82ec4 100644
--- a/subscriptionmanager.py
+++ b/subscriptionmanager.py
@@ -26,7 +26,22 @@ import rhsm.certificate as rhsm_certificate
import rhsm.config as rhsm_config
class SubscriptionManagerError(Exception):
- pass
+ def __init__(self):
+ pass
+
+class SubscriptionManagerCertError(SubscriptionManagerError):
+ def __init__(self, message):
+ self.message = message
+
+ def __str__(self):
+ return self.message
+
+class SubscriptionManagerConnectionError(SubscriptionManagerError):
+ def __init__(self, message):
+ self.message = message
+
+ def __str__(self):
+ return self.message
class SubscriptionManager:
""" Class for interacting subscription-manager. """
@@ -34,6 +49,7 @@ class SubscriptionManager:
self.logger = logger
self.cert_uuid = None
+ self.config = rhsm_config.initConfig()
self.readConfig()
# Consumer ID obtained from consumer certificate
@@ -42,26 +58,23 @@ class SubscriptionManager:
def readConfig(self):
""" Parse rhsm.conf in order to obtain consumer
certificate and key paths. """
- self.config = rhsm_config.initConfig()
consumerCertDir = self.config.get("rhsm", "consumerCertDir")
cert = 'cert.pem'
key = 'key.pem'
self.cert_file = os.path.join(consumerCertDir, cert)
self.key_file = os.path.join(consumerCertDir, key)
if not os.access(self.cert_file, os.R_OK):
- self.logger.error("Unable to read certificate, system is not registered or you are not root")
- sys.exit(1)
+ raise SubscriptionManagerCertError("Unable to read certificate, system is not registered or you are not root")
- def connect(self):
+ def connect(self, Connection=rhsm_connection.UEPConnection):
""" Connect to the subscription-manager. """
- self.connection = rhsm_connection.UEPConnection(
- cert_file=self.cert_file, key_file=self.key_file)
+ self.connection = Connection(cert_file=self.cert_file, key_file=self.key_file)
try:
if not self.connection.ping()['result']:
- self.logger.error("Unable to obtain status from server, UEPConnection is likely not usable.")
+ raise SubscriptionManagerConnectionError("Unable to obtain status from server, UEPConnection is likely not usable.")
except Exception, e:
- self.logger.exception(e)
- self.logger.error("Unable to obtain status from server, UEPConnection is likely not usable:")
+ self.logger.exception("Unable to obtain status from server, UEPConnection is likely not usable:")
+ raise SubscriptionManagerError()
def sendVirtGuests(self, domains):
""" Update consumer facts with UUIDs of virtual guests. """
@@ -72,24 +85,26 @@ class SubscriptionManager:
uuids.append(domain.UUIDString())
uuids.sort()
- self.logger.info("Sending list of uuids: %s" % uuids)
+ self.logger.debug("Sending list of uuids: %s" % uuids)
# Send list of guest uuids to the server
try:
self.connection.updateConsumer(self.uuid(), guest_uuids=uuids)
except Exception, e:
- raise SubscriptionManagerError(str(e))
+ self.logger.exception("Updating consumer failed:")
+ raise SubscriptionManagerError()
def hypervisorCheckIn(self, owner, env, mapping):
""" Send hosts to guests mapping to subscription manager. """
- self.logger.info("Sending update in hosts-to-guests mapping: %s" % mapping)
+ self.logger.debug("Sending update in hosts-to-guests mapping: %s" % mapping)
# Send the mapping
try:
return self.connection.hypervisorCheckIn(owner, env, mapping)
except Exception, e:
- raise SubscriptionManagerError(str(e))
+ self.logger.exception("Hypervisor check-in failed:")
+ raise SubscriptionManagerError()
def uuid(self):
""" Read consumer certificate and get consumer UUID from it. """
@@ -97,8 +112,8 @@ class SubscriptionManager:
try:
f = open(self.cert_file, "r")
except Exception, e:
- self.logger.error("Unable to open certificate (%s): %s" % (self.cert_file, e.message))
- return ""
+ self.logger.exception("Unable to open certificate (%s):" % self.cert_file)
+ raise SubscriptionManagerError()
certificate = rhsm_certificate.Certificate(f.read())
f.close()
self.cert_uuid = certificate.subject().get('CN')
diff --git a/virt-who.conf b/virt-who.conf
index 8916919..b52983f 100644
--- a/virt-who.conf
+++ b/virt-who.conf
@@ -1,2 +1,3 @@
+# Enviromental variables for virt-who service can be specified here
VIRTWHO_BACKGROUND=1
-VIRTWHO_DEBUG=0
\ No newline at end of file
+VIRTWHO_DEBUG=0
diff --git a/virt-who.py b/virt-who.py
index 70efdab..5593288 100644
--- a/virt-who.py
+++ b/virt-who.py
@@ -22,12 +22,13 @@ import sys
import os
import time
import atexit
+import signal
from virt import Virt, VirtError
from vdsm import VDSM
from vsphere import VSphere
from event import virEventLoopPureStart
-from subscriptionmanager import SubscriptionManager, SubscriptionManagerError
+from subscriptionmanager import SubscriptionManager, SubscriptionManagerError, SubscriptionManagerCertError
import logging
import log
@@ -58,6 +59,11 @@ class VirtWho(object):
self.virt = None
self.subscriptionManager = None
+ self.unableToRecoverStr = "Unable to recover"
+ if not options.oneshot:
+ self.unableToRecoverStr += ", retry in %d seconds." % RetryInterval
+
+
def initVirt(self):
"""
Connect to the virtualization supervisor (libvirt or VDSM)
@@ -81,11 +87,13 @@ class VirtWho(object):
self.subscriptionManager.connect()
self.tryRegisterEventCallback()
except NoOptionError, e:
- self.logger.error("Error in reading configuration file (/etc/rhsm/rhsm.conf): %s" % e)
+ self.logger.error("Error in reading configuration file (/etc/rhsm/rhsm.conf)")
# Unability to parse configuration file is fatal error, so we'll quit
sys.exit(4)
- except Exception, e:
- raise e
+ except SubscriptionManagerCertError, e:
+ self.logger.error(e.message)
+ # virt-who can't work properly without certificate, exitting
+ sys.exit(5)
def tryRegisterEventCallback(self):
"""
@@ -158,18 +166,17 @@ class VirtWho(object):
logger.error("Error in communication with virt backend, trying to recover")
return self._send(False)
else:
- logger.error("Unable to recover, retry in %d seconds." % RetryInterval)
+ logger.error(self.unableToRecoverStr)
return False
except SubscriptionManagerError, e:
# Communication with subscription manager failed
- logger.exception(e)
self.subscriptionManager = None
# Retry once
if retry:
logger.error("Error in communication with candlepin, trying to recover")
return self._send(False)
else:
- logger.error("Unable to recover, retry in %d seconds." % RetryInterval)
+ logger.error(self.unableToRecoverStr)
return False
except Exception, e:
# Some other error happens
@@ -181,7 +188,7 @@ class VirtWho(object):
logger.error("Unexcepted error occurs, trying to recover")
return self._send(False)
else:
- logger.error("Unable to recover, retry in %d seconds." % RetryInterval)
+ logger.error(self.unableToRecoverStr)
return False
def ping(self):
@@ -240,6 +247,8 @@ def daemonize(debugMode):
def createPidFile(logger):
atexit.register(cleanup)
+ signal.signal(signal.SIGINT, cleanup)
+ signal.signal(signal.SIGTERM, cleanup)
# Write pid to pidfile
try:
@@ -249,24 +258,25 @@ def createPidFile(logger):
except Exception, e:
logger.error("Unable to create pid file: %s" % str(e))
-def cleanup():
+def cleanup(sig=None, stack=None):
try:
os.remove(PIDFILE)
except Exception:
pass
+ if sig is not None and sig in [signal.SIGINT, signal.SIGTERM, signal.SIGKILL]:
+ sys.exit(0)
+
if __name__ == '__main__':
if os.access(PIDFILE, os.F_OK):
print >>sys.stderr, "virt-who seems to be already running. If not, remove %s" % PIDFILE
sys.exit(1)
- log.init_logger()
-
- logger = logging.getLogger("rhsm-app." + __name__)
-
- parser = OptionParser(description="Agent for reporting virtual guest IDs to subscription-manager")
+ parser = OptionParser(description="Agent for reporting virtual guest IDs to subscription-manager",
+ epilog="virt-who also reads enviromental variables. They have the same name as command line arguments but uppercased, with underscore instead of dash and prefixed with VIRTWHO_ (e.g. VIRTWHO_ONE_SHOT). Empty variables are considered as disabled, non-empty as enabled")
parser.add_option("-d", "--debug", action="store_true", dest="debug", default=False, help="Enable debugging output")
parser.add_option("-b", "--background", action="store_true", dest="background", default=False, help="Run in the background and monitor virtual guests")
+ parser.add_option("-o", "--one-shot", action="store_true", dest="oneshot", default=False, help="Send the list of guest IDs and exit immediately")
parser.add_option("-i", "--interval", type="int", dest="interval", default=0, help="Acquire and send list of virtual guest each N seconds")
parser.add_option("--libvirt", action="store_const", dest="virtType", const="libvirt", default="libvirt", help="Use libvirt to list virtual guests [default]")
parser.add_option("--vdsm", action="store_const", dest="virtType", const="vdsm", help="Use vdsm to list virtual guests")
@@ -288,19 +298,17 @@ if __name__ == '__main__':
env = os.getenv("VIRTWHO_DEBUG", "0").strip().lower()
if env in ["1", "true"]:
options.debug = True
- if options.debug:
- # Enable debugging output to be writen in /var/log
- logger.setLevel(logging.DEBUG)
- # Print debugging output to stderr too
- logger.addHandler(logging.StreamHandler())
- else:
- logger.setLevel(logging.INFO)
+ logger = log.getLogger(options.debug, options.background)
env = os.getenv("VIRTWHO_BACKGROUND", "0").strip().lower()
if env in ["1", "true"]:
options.background = True
+ env = os.getenv("VIRTWHO_ONE_SHOT", "0").strip().lower()
+ if env in ["1", "true"]:
+ options.oneshot = True
+
env = os.getenv("VIRTWHO_INTERVAL", "0").strip().lower()
try:
if int(env) > 0 and options.interval == 0:
@@ -344,7 +352,14 @@ if __name__ == '__main__':
logger.warning("Interval is not positive number, ignoring")
options.interval = 0
- if options.background and options.interval == 0:
+ if options.background and options.oneshot:
+ logger.error("Background and oneshot can't be used together, using background mode")
+ options.oneshot = False
+
+ if options.oneshot and options.interval > 0:
+ logger.error("Interval doesn't make sense in oneshot mode, ignoring")
+
+ if not options.oneshot and options.interval == 0:
# Interval is still used in background mode, because events can get lost
# (e.g. libvirtd restart)
options.interval = DefaultInterval
@@ -360,14 +375,23 @@ if __name__ == '__main__':
else:
logger.warning("Listening for events is not available in VDSM or ESX mode")
+ if options.interval < RetryInterval:
+ RetryInterval = options.interval
+
virtWho = VirtWho(logger, options)
- virtWho.checkConnections()
+ try:
+ virtWho.checkConnections()
+ except Exception:
+ pass
createPidFile(logger)
logger.debug("Virt-who is running in %s mode" % options.virtType)
- if options.interval > 0:
+ if options.oneshot:
+ # Send list of virtual guests and exit
+ virtWho.send()
+ else:
if options.background:
logger.debug("Starting infinite loop with %d seconds interval and event handling" % options.interval)
else:
@@ -391,6 +415,3 @@ if __name__ == '__main__':
else:
# If last send fails, new try will be sooner
time.sleep(RetryInterval)
- else:
- # Send list of virtual guests and exit
- virtWho.send()
11 years, 10 months