summaryrefslogtreecommitdiffstats
path: root/bin
diff options
context:
space:
mode:
authorMORITA Kazutaka <morita.kazutaka@gmail.com>2011-01-13 21:02:14 +0900
committerMORITA Kazutaka <morita.kazutaka@gmail.com>2011-01-13 21:02:14 +0900
commit40e13153d3a8ceef80fa40a59145672df796baa8 (patch)
tree9887fa6698a0cbae2da6211921df62308fbb1f79 /bin
parent1c694e9093c627bd50b35e9fb0ae11adf315a154 (diff)
parent621cf8e156582b3ff4dd44409324cc3a5f9aecf4 (diff)
downloadnova-40e13153d3a8ceef80fa40a59145672df796baa8.tar.gz
nova-40e13153d3a8ceef80fa40a59145672df796baa8.tar.xz
nova-40e13153d3a8ceef80fa40a59145672df796baa8.zip
Merge trunk
Diffstat (limited to 'bin')
-rwxr-xr-xbin/nova-ajax-console-proxy137
-rwxr-xr-xbin/nova-api1
-rwxr-xr-xbin/nova-api-paste109
-rwxr-xr-xbin/nova-combined1
-rwxr-xr-xbin/nova-console44
-rwxr-xr-xbin/nova-dhcpbridge17
-rwxr-xr-xbin/nova-instancemonitor7
-rw-r--r--bin/nova-logspool156
-rwxr-xr-xbin/nova-manage72
-rw-r--r--bin/nova-spoolsentry97
10 files changed, 625 insertions, 16 deletions
diff --git a/bin/nova-ajax-console-proxy b/bin/nova-ajax-console-proxy
new file mode 100755
index 000000000..2bc407658
--- /dev/null
+++ b/bin/nova-ajax-console-proxy
@@ -0,0 +1,137 @@
+#!/usr/bin/env python
+# pylint: disable-msg=C0103
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright 2010 United States Government as represented by the
+# Administrator of the National Aeronautics and Space Administration.
+# All Rights Reserved.
+#
+# 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.
+
+"""Ajax Console Proxy Server"""
+
+from eventlet import greenthread
+from eventlet.green import urllib2
+
+import exceptions
+import gettext
+import logging
+import os
+import sys
+import time
+import urlparse
+
+# If ../nova/__init__.py exists, add ../ to Python search path, so that
+# it will override what happens to be installed in /usr/(local/)lib/python...
+possible_topdir = os.path.normpath(os.path.join(os.path.abspath(sys.argv[0]),
+ os.pardir,
+ os.pardir))
+if os.path.exists(os.path.join(possible_topdir, 'nova', '__init__.py')):
+ sys.path.insert(0, possible_topdir)
+
+gettext.install('nova', unicode=1)
+
+from nova import flags
+from nova import log as logging
+from nova import rpc
+from nova import utils
+from nova import wsgi
+
+FLAGS = flags.FLAGS
+
+flags.DEFINE_integer('ajax_console_idle_timeout', 300,
+ 'Seconds before idle connection destroyed')
+
+LOG = logging.getLogger('nova.ajax_console_proxy')
+LOG.setLevel(logging.DEBUG)
+LOG.addHandler(logging.StreamHandler())
+
+
+class AjaxConsoleProxy(object):
+ tokens = {}
+
+ def __call__(self, env, start_response):
+ try:
+ req_url = '%s://%s%s?%s' % (env['wsgi.url_scheme'],
+ env['HTTP_HOST'],
+ env['PATH_INFO'],
+ env['QUERY_STRING'])
+ if 'HTTP_REFERER' in env:
+ auth_url = env['HTTP_REFERER']
+ else:
+ auth_url = req_url
+
+ auth_params = urlparse.parse_qs(urlparse.urlparse(auth_url).query)
+ parsed_url = urlparse.urlparse(req_url)
+
+ auth_info = AjaxConsoleProxy.tokens[auth_params['token'][0]]
+ args = auth_info['args']
+ auth_info['last_activity'] = time.time()
+
+ remote_url = ("http://%s:%s%s?token=%s" % (
+ str(args['host']),
+ str(args['port']),
+ parsed_url.path,
+ str(args['token'])))
+
+ opener = urllib2.urlopen(remote_url, env['wsgi.input'].read())
+ body = opener.read()
+ info = opener.info()
+
+ start_response("200 OK", info.dict.items())
+ return body
+ except (exceptions.KeyError):
+ if env['PATH_INFO'] != '/favicon.ico':
+ LOG.audit("Unauthorized request %s, %s"
+ % (req_url, str(env)))
+ start_response("401 NOT AUTHORIZED", [])
+ return "Not Authorized"
+ except Exception:
+ start_response("500 ERROR", [])
+ return "Server Error"
+
+ def register_listeners(self):
+ class Callback:
+ def __call__(self, data, message):
+ if data['method'] == 'authorize_ajax_console':
+ AjaxConsoleProxy.tokens[data['args']['token']] = \
+ {'args': data['args'], 'last_activity': time.time()}
+
+ conn = rpc.Connection.instance(new=True)
+ consumer = rpc.TopicConsumer(
+ connection=conn,
+ topic=FLAGS.ajax_console_proxy_topic)
+ consumer.register_callback(Callback())
+
+ def delete_expired_tokens():
+ now = time.time()
+ to_delete = []
+ for k, v in AjaxConsoleProxy.tokens.items():
+ if now - v['last_activity'] > FLAGS.ajax_console_idle_timeout:
+ to_delete.append(k)
+
+ for k in to_delete:
+ del AjaxConsoleProxy.tokens[k]
+
+ utils.LoopingCall(consumer.fetch, auto_ack=True,
+ enable_callbacks=True).start(0.1)
+ utils.LoopingCall(delete_expired_tokens).start(1)
+
+if __name__ == '__main__':
+ utils.default_flagfile()
+ FLAGS(sys.argv)
+ server = wsgi.Server()
+ acp = AjaxConsoleProxy()
+ acp.register_listeners()
+ server.start(acp, FLAGS.ajax_console_proxy_port, host='0.0.0.0')
+ server.wait()
diff --git a/bin/nova-api b/bin/nova-api
index 1c671201e..c7cbb3ab4 100755
--- a/bin/nova-api
+++ b/bin/nova-api
@@ -41,7 +41,6 @@ from nova import wsgi
FLAGS = flags.FLAGS
-flags.DEFINE_integer('osapi_port', 8774, 'OpenStack API port')
flags.DEFINE_string('osapi_host', '0.0.0.0', 'OpenStack API host')
flags.DEFINE_integer('ec2api_port', 8773, 'EC2 API port')
flags.DEFINE_string('ec2api_host', '0.0.0.0', 'EC2 API host')
diff --git a/bin/nova-api-paste b/bin/nova-api-paste
new file mode 100755
index 000000000..419f0bbdc
--- /dev/null
+++ b/bin/nova-api-paste
@@ -0,0 +1,109 @@
+#!/usr/bin/env python
+# pylint: disable-msg=C0103
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright 2010 United States Government as represented by the
+# Administrator of the National Aeronautics and Space Administration.
+# All Rights Reserved.
+#
+# 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.
+
+"""Starter script for Nova API."""
+
+import gettext
+import os
+import sys
+
+from paste import deploy
+
+# If ../nova/__init__.py exists, add ../ to Python search path, so that
+# it will override what happens to be installed in /usr/(local/)lib/python...
+possible_topdir = os.path.normpath(os.path.join(os.path.abspath(sys.argv[0]),
+ os.pardir,
+ os.pardir))
+if os.path.exists(os.path.join(possible_topdir, 'nova', '__init__.py')):
+ sys.path.insert(0, possible_topdir)
+
+gettext.install('nova', unicode=1)
+
+from nova import flags
+from nova import log as logging
+from nova import wsgi
+
+LOG = logging.getLogger('nova.api')
+LOG.setLevel(logging.DEBUG)
+LOG.addHandler(logging.StreamHandler())
+
+FLAGS = flags.FLAGS
+
+API_ENDPOINTS = ['ec2', 'openstack']
+
+
+def load_configuration(paste_config):
+ """Load the paste configuration from the config file and return it."""
+ config = None
+ # Try each known name to get the global DEFAULTS, which will give ports
+ for name in API_ENDPOINTS:
+ try:
+ config = deploy.appconfig("config:%s" % paste_config, name=name)
+ except LookupError:
+ pass
+ if config:
+ verbose = config.get('verbose', None)
+ if verbose:
+ FLAGS.verbose = int(verbose) == 1
+ if FLAGS.verbose:
+ logging.getLogger().setLevel(logging.DEBUG)
+ return config
+ LOG.debug(_("Paste config at %s has no secion for known apis"),
+ paste_config)
+ print _("Paste config at %s has no secion for any known apis") % \
+ paste_config
+ os.exit(1)
+
+
+def launch_api(paste_config_file, section, server, port, host):
+ """Launch an api server from the specified port and IP."""
+ LOG.debug(_("Launching %s api on %s:%s"), section, host, port)
+ app = deploy.loadapp('config:%s' % paste_config_file, name=section)
+ server.start(app, int(port), host)
+
+
+def run_app(paste_config_file):
+ LOG.debug(_("Using paste.deploy config at: %s"), configfile)
+ config = load_configuration(paste_config_file)
+ LOG.debug(_("Configuration: %r"), config)
+ server = wsgi.Server()
+ ip = config.get('host', '0.0.0.0')
+ for api in API_ENDPOINTS:
+ port = config.get("%s_port" % api, None)
+ if not port:
+ continue
+ host = config.get("%s_host" % api, ip)
+ launch_api(configfile, api, server, port, host)
+ LOG.debug(_("All api servers launched, now waiting"))
+ server.wait()
+
+
+if __name__ == '__main__':
+ FLAGS(sys.argv)
+ configfiles = ['/etc/nova/nova-api.conf']
+ if os.path.exists(os.path.join(possible_topdir, 'nova', '__init__.py')):
+ configfiles.insert(0,
+ os.path.join(possible_topdir, 'etc', 'nova-api.conf'))
+ for configfile in configfiles:
+ if os.path.exists(configfile):
+ run_app(configfile)
+ break
+ else:
+ LOG.debug(_("Skipping missing configuration: %s"), configfile)
diff --git a/bin/nova-combined b/bin/nova-combined
index 53322f1a0..f932fdfd5 100755
--- a/bin/nova-combined
+++ b/bin/nova-combined
@@ -44,7 +44,6 @@ from nova import wsgi
FLAGS = flags.FLAGS
-flags.DEFINE_integer('osapi_port', 8774, 'OpenStack API port')
flags.DEFINE_string('osapi_host', '0.0.0.0', 'OpenStack API host')
flags.DEFINE_integer('ec2api_port', 8773, 'EC2 API port')
flags.DEFINE_string('ec2api_host', '0.0.0.0', 'EC2 API host')
diff --git a/bin/nova-console b/bin/nova-console
new file mode 100755
index 000000000..802cc80b6
--- /dev/null
+++ b/bin/nova-console
@@ -0,0 +1,44 @@
+#!/usr/bin/env python
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright (c) 2010 Openstack, LLC.
+# All Rights Reserved.
+#
+# 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.
+
+"""Starter script for Nova Console Proxy."""
+
+import eventlet
+eventlet.monkey_patch()
+
+import gettext
+import os
+import sys
+
+# If ../nova/__init__.py exists, add ../ to Python search path, so that
+# it will override what happens to be installed in /usr/(local/)lib/python...
+possible_topdir = os.path.normpath(os.path.join(os.path.abspath(sys.argv[0]),
+ os.pardir,
+ os.pardir))
+if os.path.exists(os.path.join(possible_topdir, 'nova', '__init__.py')):
+ sys.path.insert(0, possible_topdir)
+
+gettext.install('nova', unicode=1)
+
+from nova import service
+from nova import utils
+
+if __name__ == '__main__':
+ utils.default_flagfile()
+ service.serve()
+ service.wait()
diff --git a/bin/nova-dhcpbridge b/bin/nova-dhcpbridge
index 828aba3d1..1a994d956 100755
--- a/bin/nova-dhcpbridge
+++ b/bin/nova-dhcpbridge
@@ -22,7 +22,6 @@ Handle lease database updates from DHCP servers.
"""
import gettext
-import logging
import os
import sys
@@ -39,6 +38,7 @@ gettext.install('nova', unicode=1)
from nova import context
from nova import db
from nova import flags
+from nova import log as logging
from nova import rpc
from nova import utils
from nova.network import linux_net
@@ -49,11 +49,13 @@ flags.DECLARE('network_size', 'nova.network.manager')
flags.DECLARE('num_networks', 'nova.network.manager')
flags.DECLARE('update_dhcp_on_disassociate', 'nova.network.manager')
+LOG = logging.getLogger('nova.dhcpbridge')
+
def add_lease(mac, ip_address, _hostname, _interface):
"""Set the IP that was assigned by the DHCP server."""
if FLAGS.fake_rabbit:
- logging.debug("leasing ip")
+ LOG.debug(_("leasing ip"))
network_manager = utils.import_object(FLAGS.network_manager)
network_manager.lease_fixed_ip(context.get_admin_context(),
mac,
@@ -68,14 +70,14 @@ def add_lease(mac, ip_address, _hostname, _interface):
def old_lease(mac, ip_address, hostname, interface):
"""Update just as add lease."""
- logging.debug("Adopted old lease or got a change of mac/hostname")
+ LOG.debug(_("Adopted old lease or got a change of mac/hostname"))
add_lease(mac, ip_address, hostname, interface)
def del_lease(mac, ip_address, _hostname, _interface):
"""Called when a lease expires."""
if FLAGS.fake_rabbit:
- logging.debug("releasing ip")
+ LOG.debug(_("releasing ip"))
network_manager = utils.import_object(FLAGS.network_manager)
network_manager.release_fixed_ip(context.get_admin_context(),
mac,
@@ -100,6 +102,7 @@ def main():
flagfile = os.environ.get('FLAGFILE', FLAGS.dhcpbridge_flagfile)
utils.default_flagfile(flagfile)
argv = FLAGS(sys.argv)
+ logging.basicConfig()
interface = os.environ.get('DNSMASQ_INTERFACE', 'br0')
if int(os.environ.get('TESTING', '0')):
FLAGS.fake_rabbit = True
@@ -117,9 +120,9 @@ def main():
mac = argv[2]
ip = argv[3]
hostname = argv[4]
- logging.debug("Called %s for mac %s with ip %s and "
- "hostname %s on interface %s",
- action, mac, ip, hostname, interface)
+ LOG.debug(_("Called %s for mac %s with ip %s and "
+ "hostname %s on interface %s"),
+ action, mac, ip, hostname, interface)
globals()[action + '_lease'](mac, ip, hostname, interface)
else:
print init_leases(interface)
diff --git a/bin/nova-instancemonitor b/bin/nova-instancemonitor
index 5dac3ffe6..7dca02014 100755
--- a/bin/nova-instancemonitor
+++ b/bin/nova-instancemonitor
@@ -23,7 +23,6 @@
import gettext
import os
-import logging
import sys
from twisted.application import service
@@ -37,19 +36,23 @@ if os.path.exists(os.path.join(possible_topdir, 'nova', '__init__.py')):
gettext.install('nova', unicode=1)
+from nova import log as logging
from nova import utils
from nova import twistd
from nova.compute import monitor
+# TODO(todd): shouldn't this be done with flags? And what about verbose?
logging.getLogger('boto').setLevel(logging.WARN)
+LOG = logging.getLogger('nova.instancemonitor')
+
if __name__ == '__main__':
utils.default_flagfile()
twistd.serve(__file__)
if __name__ == '__builtin__':
- logging.warn('Starting instance monitor')
+ LOG.warn(_('Starting instance monitor'))
# pylint: disable-msg=C0103
monitor = monitor.InstanceMonitor()
diff --git a/bin/nova-logspool b/bin/nova-logspool
new file mode 100644
index 000000000..097459b12
--- /dev/null
+++ b/bin/nova-logspool
@@ -0,0 +1,156 @@
+#!/usr/bin/env python
+
+# Copyright 2010 United States Government as represented by the
+# Administrator of the National Aeronautics and Space Administration.
+# All Rights Reserved.
+#
+# 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.
+
+"""
+Tools for working with logs generated by nova components
+"""
+
+
+import json
+import os
+import re
+import sys
+
+
+class Request(object):
+ def __init__(self):
+ self.time = ""
+ self.host = ""
+ self.logger = ""
+ self.message = ""
+ self.trace = ""
+ self.env = ""
+ self.request_id = ""
+
+ def add_error_line(self, error_line):
+ self.time = " ".join(error_line.split(" ")[:3])
+ self.host = error_line.split(" ")[3]
+ self.logger = error_line.split("(")[1].split(" ")[0]
+ self.request_id = error_line.split("[")[1].split(" ")[0]
+ error_lines = error_line.split("#012")
+ self.message = self.clean_log_line(error_lines.pop(0))
+ self.trace = "\n".join([self.clean_trace(l) for l in error_lines])
+
+ def add_environment_line(self, env_line):
+ self.env = self.clean_env_line(env_line)
+
+ def clean_log_line(self, line):
+ """Remove log format for time, level, etc: split after context"""
+ return line.split('] ')[-1]
+
+ def clean_env_line(self, line):
+ """Also has an 'Environment: ' string in the message"""
+ return re.sub(r'^Environment: ', '', self.clean_log_line(line))
+
+ def clean_trace(self, line):
+ """trace has a different format, so split on TRACE:"""
+ return line.split('TRACE: ')[-1]
+
+ def to_dict(self):
+ return {'traceback': self.trace, 'message': self.message,
+ 'host': self.host, 'env': self.env, 'logger': self.logger,
+ 'request_id': self.request_id}
+
+
+class LogReader(object):
+ def __init__(self, filename):
+ self.filename = filename
+ self._errors = {}
+
+ def process(self, spooldir):
+ with open(self.filename) as f:
+ line = f.readline()
+ while len(line) > 0:
+ parts = line.split(" ")
+ level = (len(parts) < 6) or parts[5]
+ if level == 'ERROR':
+ self.handle_logged_error(line)
+ elif level == '[-]' and self.last_error:
+ # twisted stack trace line
+ clean_line = " ".join(line.split(" ")[6:])
+ self.last_error.trace = self.last_error.trace + clean_line
+ else:
+ self.last_error = None
+ line = f.readline()
+ self.update_spool(spooldir)
+
+ def handle_logged_error(self, line):
+ request_id = re.search(r' \[([A-Z0-9\-/]+)', line)
+ if not request_id:
+ raise Exception("Unable to parse request id from %s" % line)
+ request_id = request_id.group(1)
+ data = self._errors.get(request_id, Request())
+ if self.is_env_line(line):
+ data.add_environment_line(line)
+ elif self.is_error_line(line):
+ data.add_error_line(line)
+ else:
+ # possibly error from twsited
+ data.add_error_line(line)
+ self.last_error = data
+ self._errors[request_id] = data
+
+ def is_env_line(self, line):
+ return re.search('Environment: ', line)
+
+ def is_error_line(self, line):
+ return re.search('raised', line)
+
+ def update_spool(self, directory):
+ processed_dir = "%s/processed" % directory
+ self._ensure_dir_exists(processed_dir)
+ for rid, value in self._errors.iteritems():
+ if not self.has_been_processed(processed_dir, rid):
+ with open("%s/%s" % (directory, rid), "w") as spool:
+ spool.write(json.dumps(value.to_dict()))
+ self.flush_old_processed_spool(processed_dir)
+
+ def _ensure_dir_exists(self, d):
+ mkdir = False
+ try:
+ os.stat(d)
+ except:
+ mkdir = True
+ if mkdir:
+ os.mkdir(d)
+
+ def has_been_processed(self, processed_dir, rid):
+ rv = False
+ try:
+ os.stat("%s/%s" % (processed_dir, rid))
+ rv = True
+ except:
+ pass
+ return rv
+
+ def flush_old_processed_spool(self, processed_dir):
+ keys = self._errors.keys()
+ procs = os.listdir(processed_dir)
+ for p in procs:
+ if p not in keys:
+ # log has rotated and the old error won't be seen again
+ os.unlink("%s/%s" % (processed_dir, p))
+
+if __name__ == '__main__':
+ filename = '/var/log/nova.log'
+ spooldir = '/var/spool/nova'
+ if len(sys.argv) > 1:
+ filename = sys.argv[1]
+ if len(sys.argv) > 2:
+ spooldir = sys.argv[2]
+ LogReader(filename).process(spooldir)
diff --git a/bin/nova-manage b/bin/nova-manage
index 599e02a7e..3e290567c 100755
--- a/bin/nova-manage
+++ b/bin/nova-manage
@@ -53,9 +53,10 @@
CLI interface for nova management.
"""
+import datetime
import gettext
-import logging
import os
+import re
import sys
import time
@@ -76,12 +77,14 @@ from nova import crypto
from nova import db
from nova import exception
from nova import flags
+from nova import log as logging
from nova import quota
from nova import utils
from nova.auth import manager
from nova.cloudpipe import pipelib
+logging.basicConfig()
FLAGS = flags.FLAGS
flags.DECLARE('fixed_range', 'nova.network.manager')
flags.DECLARE('num_networks', 'nova.network.manager')
@@ -332,6 +335,11 @@ class ProjectCommands(object):
arguments: name project_manager [description]"""
self.manager.create_project(name, project_manager, description)
+ def modify(self, name, project_manager, description=None):
+ """Modifies a project
+ arguments: name project_manager [description]"""
+ self.manager.modify_project(name, project_manager, description)
+
def delete(self, name):
"""Deletes an existing project
arguments: name"""
@@ -452,6 +460,61 @@ class NetworkCommands(object):
int(network_size), int(vlan_start),
int(vpn_start))
+
+class ServiceCommands(object):
+ """Enable and disable running services"""
+
+ def list(self, host=None, service=None):
+ """Show a list of all running services. Filter by host & service name.
+ args: [host] [service]"""
+ ctxt = context.get_admin_context()
+ now = datetime.datetime.utcnow()
+ services = db.service_get_all(ctxt)
+ if host:
+ services = [s for s in services if s['host'] == host]
+ if service:
+ services = [s for s in services if s['binary'] == service]
+ for svc in services:
+ delta = now - (svc['updated_at'] or svc['created_at'])
+ alive = (delta.seconds <= 15)
+ art = (alive and ":-)") or "XXX"
+ active = 'enabled'
+ if svc['disabled']:
+ active = 'disabled'
+ print "%-10s %-10s %-8s %s %s" % (svc['host'], svc['binary'],
+ active, art,
+ svc['updated_at'])
+
+ def enable(self, host, service):
+ """Enable scheduling for a service
+ args: host service"""
+ ctxt = context.get_admin_context()
+ svc = db.service_get_by_args(ctxt, host, service)
+ if not svc:
+ print "Unable to find service"
+ return
+ db.service_update(ctxt, svc['id'], {'disabled': False})
+
+ def disable(self, host, service):
+ """Disable scheduling for a service
+ args: host service"""
+ ctxt = context.get_admin_context()
+ svc = db.service_get_by_args(ctxt, host, service)
+ if not svc:
+ print "Unable to find service"
+ return
+ db.service_update(ctxt, svc['id'], {'disabled': True})
+
+
+class LogCommands(object):
+ def request(self, request_id, logfile='/var/log/nova.log'):
+ """Show all fields in the log for the given request. Assumes you
+ haven't changed the log format too much.
+ ARGS: request_id [logfile]"""
+ lines = utils.execute("cat %s | grep '\[%s '" % (logfile, request_id))
+ print re.sub('#012', "\n", "\n".join(lines))
+
+
CATEGORIES = [
('user', UserCommands),
('project', ProjectCommands),
@@ -459,7 +522,9 @@ CATEGORIES = [
('shell', ShellCommands),
('vpn', VpnCommands),
('floating', FloatingIpCommands),
- ('network', NetworkCommands)]
+ ('network', NetworkCommands),
+ ('service', ServiceCommands),
+ ('log', LogCommands)]
def lazy_match(name, key_value_tuples):
@@ -498,9 +563,6 @@ def main():
utils.default_flagfile()
argv = FLAGS(sys.argv)
- if FLAGS.verbose:
- logging.getLogger().setLevel(logging.DEBUG)
-
script_name = argv.pop(0)
if len(argv) < 1:
print script_name + " category action [<args>]"
diff --git a/bin/nova-spoolsentry b/bin/nova-spoolsentry
new file mode 100644
index 000000000..ab20268a9
--- /dev/null
+++ b/bin/nova-spoolsentry
@@ -0,0 +1,97 @@
+#!/usr/bin/env python
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright 2010 United States Government as represented by the
+# Administrator of the National Aeronautics and Space Administration.
+# All Rights Reserved.
+#
+# 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.
+
+
+import base64
+import json
+import logging
+import os
+import shutil
+import sys
+import urllib
+import urllib2
+try:
+ import cPickle as pickle
+except:
+ import pickle
+
+
+class SpoolSentry(object):
+ def __init__(self, spool_dir, sentry_url, key=None):
+ self.spool_dir = spool_dir
+ self.sentry_url = sentry_url
+ self.key = key
+
+ def process(self):
+ for fname in os.listdir(self.spool_dir):
+ if fname == "processed":
+ continue
+ try:
+ sourcefile = "%s/%s" % (self.spool_dir, fname)
+ with open(sourcefile) as f:
+ fdata = f.read()
+ data_from_json = json.loads(fdata)
+ data = self.build_data(data_from_json)
+ self.send_data(data)
+ destfile = "%s/processed/%s" % (self.spool_dir, fname)
+ shutil.move(sourcefile, destfile)
+ except:
+ logging.exception("Unable to upload record %s", fname)
+ raise
+
+ def build_data(self, filejson):
+ env = {'SERVER_NAME': 'unknown', 'SERVER_PORT': '0000',
+ 'SCRIPT_NAME': '/unknown/', 'PATH_INFO': 'unknown'}
+ if filejson['env']:
+ env = json.loads(filejson['env'])
+ url = "http://%s:%s%s%s" % (env['SERVER_NAME'], env['SERVER_PORT'],
+ env['SCRIPT_NAME'], env['PATH_INFO'])
+ rv = {'logger': filejson['logger'], 'level': logging.ERROR,
+ 'server_name': filejson['host'], 'url': url,
+ 'message': filejson['message'],
+ 'traceback': filejson['traceback']}
+ rv['data'] = {}
+ if filejson['env']:
+ rv['data']['META'] = env
+ if filejson['request_id']:
+ rv['data']['request_id'] = filejson['request_id']
+ return rv
+
+ def send_data(self, data):
+ data = {
+ 'data': base64.b64encode(pickle.dumps(data).encode('zlib')),
+ 'key': self.key
+ }
+ req = urllib2.Request(self.sentry_url)
+ res = urllib2.urlopen(req, urllib.urlencode(data))
+ if res.getcode() != 200:
+ raise Exception("Bad HTTP code: %s" % res.getcode())
+ txt = res.read()
+
+if __name__ == '__main__':
+ sentryurl = 'http://127.0.0.1/sentry/store/'
+ key = ''
+ spooldir = '/var/spool/nova'
+ if len(sys.argv) > 1:
+ sentryurl = sys.argv[1]
+ if len(sys.argv) > 2:
+ key = sys.argv[2]
+ if len(sys.argv) > 3:
+ spooldir = sys.argv[3]
+ SpoolSentry(spooldir, sentryurl, key).process()