From: Tomas Sedovic Date: Mon, 14 May 2012 11:56:27 +0000 (+0200) Subject: Set cfn-hup to send events to the metadata server X-Git-Tag: 2014.1~1830 X-Git-Url: https://review.fuel-infra.org/gitweb?a=commitdiff_plain;h=596b74d72a76a41356a70026570f77c1d2fd037b;p=openstack-build%2Fheat-build.git Set cfn-hup to send events to the metadata server Fixes #107 This is implemented using a logging handler that sends events to the metadata server. Signed-off-by: Tomas Sedovic --- diff --git a/heat/cfntools/cfn-hup b/heat/cfntools/cfn-hup index d4ea2897..b81a727b 100755 --- a/heat/cfntools/cfn-hup +++ b/heat/cfntools/cfn-hup @@ -54,7 +54,7 @@ if args.verbose: else: logging.basicConfig(format=log_format, level=logging.INFO) -logger = logging.getLogger('cfn-hup') +logger = logging.getLogger('cfntools') log_file_name = "/var/log/cfn-hup.log" file_handler = logging.FileHandler(log_file_name) file_handler.setFormatter(logging.Formatter(log_format)) @@ -99,6 +99,13 @@ if not mainconfig.unique_resources_get(): args.config_dir)) exit(1) + +metadata_handler = MetadataLoggingHandler(metadata_server_url(), + mainconfig.stack, + mainconfig.unique_resources_get()[0]) +logger.addHandler(metadata_handler) + + for r in mainconfig.unique_resources_get(): print r metadata = Metadata(mainconfig.stack, diff --git a/heat/cfntools/cfn_helper.py b/heat/cfntools/cfn_helper.py index 4ff7745c..871cb28f 100644 --- a/heat/cfntools/cfn_helper.py +++ b/heat/cfntools/cfn_helper.py @@ -41,10 +41,13 @@ import rpmUtils.updates as rpmupdates import rpmUtils.miscutils as rpmutils import subprocess import sys -from urllib2 import urlopen +from urllib2 import urlopen, Request from urlparse import urlparse, urlunparse +logger = logging.getLogger('cfntools') + + def to_boolean(b): val = b.lower().strip() if isinstance(b, basestring) else b return val in [True, 'true', 'yes', '1', 1] @@ -119,7 +122,7 @@ class Hook(object): ev_name in self.triggers: CommandRunner(self.action).run(user=self.runas) else: - logging.debug('event: {%s, %s, %s} did not match %s' % \ + logger.debug('event: {%s, %s, %s} did not match %s' % \ (ev_name, ev_object, ev_resource, self.__str__())) def __str__(self): @@ -160,7 +163,7 @@ class CommandRunner(object): Returns: self """ - logging.debug("Running command: %s" % self._command) + logger.debug("Running command: %s" % self._command) cmd = ['su', user, '-c', self._command] subproc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE) @@ -299,14 +302,14 @@ class RpmHelper(object): if rpms: cmd = "rpm -U --force --nosignature " cmd += " ".join(packages) - logging.info("Installing packages: %s" % cmd) + logger.info("Installing packages: %s" % cmd) else: cmd = "yum -y install " cmd += " ".join(packages) - logging.info("Installing packages: %s" % cmd) + logger.info("Installing packages: %s" % cmd) command = CommandRunner(cmd).run() if command.status: - logging.warn("Failed to install packages: %s" % cmd) + logger.warn("Failed to install packages: %s" % cmd) @classmethod def downgrade(cls, packages, rpms=True): @@ -330,10 +333,10 @@ class RpmHelper(object): else: cmd = "yum -y downgrade " cmd += " ".join(packages) - logging.info("Downgrading packages: %s" % cmd) + logger.info("Downgrading packages: %s" % cmd) command = Command(cmd).run() if command.status: - logging.warn("Failed to downgrade packages: %s" % cmd) + logger.warn("Failed to downgrade packages: %s" % cmd) class PackagesHandler(object): @@ -418,7 +421,7 @@ class PackagesHandler(object): # FIXME:print non-error, but skipping pkg pass elif not RpmHelper.yum_package_available(pkg): - logging.warn("Skipping package '%s'. Not available via yum" % \ + logger.warn("Skipping package '%s'. Not available via yum" % \ pkg) elif not ver: installs.append(pkg) @@ -495,7 +498,7 @@ class PackagesHandler(object): for manager, package_entries in packages: handler = self._package_handler(manager) if not handler: - logging.warn("Skipping invalid package type: %s" % manager) + logger.warn("Skipping invalid package type: %s" % manager) else: handler(self, package_entries) @@ -513,9 +516,9 @@ class FilesHandler(object): os.makedirs(os.path.dirname(dest)) except OSError as e: if e.errno == errno.EEXIST: - logging.debug(str(e)) + logger.debug(str(e)) else: - logging.exception(e) + logger.exception(e) if 'content' in meta: if isinstance(meta['content'], basestring): @@ -529,7 +532,7 @@ class FilesHandler(object): elif 'source' in meta: CommandRunner('wget -O %s %s' % (dest, meta['source'])).run() else: - logging.error('%s %s' % (dest, str(meta))) + logger.error('%s %s' % (dest, str(meta))) continue uid = -1 @@ -600,9 +603,9 @@ class SourcesHandler(object): os.makedirs(dest) except OSError as e: if e.errno == errno.EEXIST: - logging.debug(str(e)) + logger.debug(str(e)) else: - logging.exception(e) + logger.exception(e) self._decompress(tmp_name, dest) @@ -654,10 +657,10 @@ class ServicesHandler(object): if "enabled" in properties: enable = to_boolean(properties["enabled"]) if enable: - logging.info("Enabling service %s" % service) + logger.info("Enabling service %s" % service) handler(self, service, "enable") else: - logging.info("Disabling service %s" % service) + logger.info("Disabling service %s" % service) handler(self, service, "disable") if "ensureRunning" in properties: @@ -665,10 +668,10 @@ class ServicesHandler(object): command = handler(self, service, "status") running = command.status == 0 if ensure_running and not running: - logging.info("Starting service %s" % service) + logger.info("Starting service %s" % service) handler(self, service, "start") elif not ensure_running and running: - logging.info("Stopping service %s" % service) + logger.info("Stopping service %s" % service) handler(self, service, "stop") def _monitor_service(self, handler, service, properties): @@ -677,10 +680,10 @@ class ServicesHandler(object): command = handler(self, service, "status") running = command.status == 0 if ensure_running and not running: - logging.info("Restarting service %s" % service) + logger.warn("Restarting service %s" % service) start_cmd = handler(self, service, "start") if start_cmd.status != 0: - logging.warning('Service %s did not start. STDERR: %s' % + logger.warning('Service %s did not start. STDERR: %s' % (service, start_cmd.stderr)) return for h in self.hooks: @@ -716,7 +719,7 @@ class ServicesHandler(object): for manager, service_entries in self._services.iteritems(): handler = self._service_handler(manager) if not handler: - logging.warn("Skipping invalid service type: %s" % manager) + logger.warn("Skipping invalid service type: %s" % manager) else: self._initialize_services(handler, service_entries) @@ -729,11 +732,56 @@ class ServicesHandler(object): for manager, service_entries in self._services.iteritems(): handler = self._service_handler(manager) if not handler: - logging.warn("Skipping invalid service type: %s" % manager) + logger.warn("Skipping invalid service type: %s" % manager) else: self._monitor_services(handler, service_entries) +def metadata_server_url(): + """ + Return the url to the metadata server. + """ + try: + f = open("/var/lib/cloud/data/cfn-metadata-server") + server_url = f.read().strip() + f.close() + if not server_url[-1] == '/': + server_url += '/' + return server_url + except IOError: + return None + + +class MetadataLoggingHandler(logging.Handler): + def __init__(self, metadata_server_url, stack, resource): + super(MetadataLoggingHandler, self).__init__(level=logging.WARNING) + self.stack = stack + self.resource = resource + + if metadata_server_url: + self.events_url = metadata_server_url + 'events/' + else: + logger.info('Metadata server URL not found') + self.events_url = None + + def handle(self, record): + if not self.events_url: + return + event = { + 'message': record.message, + 'stack': self.stack, + 'resource': self.resource, + 'resource_type': 'AWS::EC2::Instance', + 'reason': 'cfntools notification', + } + req = Request(self.events_url, json.dumps(event)) + req.headers['Content-Type'] = 'application/json' + try: + urlopen(req) + except: + pass + + class MetadataServerConnectionError(Exception): pass @@ -756,31 +804,23 @@ class Metadata(object): self._is_local_metadata = True self._metadata = None - def metadata_server_url(self): - """ - Return the url to the metadata server. - """ - try: - f = open("/var/lib/cloud/data/cfn-metadata-server") - server_url = f.read() - f.close() - except IOError: - return None - - url_parts = list(urlparse(server_url)) - url_parts[2] = '/stacks/%s/resources/%s' % (self.stack, self.resource) - return urlunparse(url_parts) + def metadata_resource_url(self): + server_url = metadata_server_url() + if not server_url: + return + return server_url + 'stacks/%s/resources/%s' % (self.stack, + self.resource) def remote_metadata(self): """ Connect to the metadata server and retreive the metadata from there. """ - server_url = self.metadata_server_url() - if not server_url: + url = self.metadata_resource_url() + if not url: raise MetadataServerConnectionError() try: - return urlopen(server_url).read() + return urlopen(url).read() except: raise MetadataServerConnectionError() diff --git a/heat/engine/manager.py b/heat/engine/manager.py index 02f4578a..c97e207f 100644 --- a/heat/engine/manager.py +++ b/heat/engine/manager.py @@ -240,17 +240,29 @@ class EngineManager(manager.Manager): return {'events': [self.parse_event(e) for e in events]} def event_create(self, context, event): + stack_name = event['stack'] + resource_name = event['resource'] + stack = db_api.stack_get(None, stack_name) + resource = db_api.resource_get_by_name_and_stack(None, resource_name, + stack.id) + if not resource: + return ['Unknown resource', None] + new_event = { + 'name': event['message'], + 'resource_status_reason': event['reason'], + 'stack_id': stack.id, + 'logical_resource_id': resource.name, + 'physical_resource_id': None, + 'resource_type': event['resource_type'], + 'resource_properties': {}, + } try: - result = db_api.event_create(None, event) - event['id'] = result.id - return [None, event] + result = db_api.event_create(None, new_event) + new_event['id'] = result.id + return [None, new_event] except Exception as ex: logger.warn('db error %s' % str(ex)) - try: - # This returns the error message without the entire SQL request - msg = ex.inner_exception.orig[1] - except: - msg = 'Error creating event' + msg = 'Error creating event' return [msg, None] def metadata_register_address(self, context, url):