Removes lockfile and custom python-daemon server initialization
in favour of paste.deploy.
This commit is contained in:
@@ -2,3 +2,8 @@
|
||||
glance.egg-info
|
||||
glance.sqlite
|
||||
*.glance-venv
|
||||
dist/
|
||||
ChangeLog
|
||||
*.pid
|
||||
*.log
|
||||
glance/vcsversion.py
|
||||
|
||||
@@ -32,8 +32,7 @@ sys.path.append(ROOT_DIR)
|
||||
|
||||
from glance import version
|
||||
from glance.common import config
|
||||
from glance.common import server
|
||||
import glance.store
|
||||
from glance.common import wsgi
|
||||
|
||||
|
||||
def create_options(parser):
|
||||
@@ -43,52 +42,22 @@ def create_options(parser):
|
||||
|
||||
:param parser: The option parser
|
||||
"""
|
||||
parser.add_option('-H', '--host',
|
||||
dest="host", metavar="ADDRESS",
|
||||
default="0.0.0.0",
|
||||
help="Address of Glance API server. "
|
||||
"Default: %default")
|
||||
parser.add_option('-p', '--port',
|
||||
dest="port", metavar="PORT", type=int,
|
||||
default=9292,
|
||||
help="Port the Glance API server listens on. "
|
||||
"Default: %default")
|
||||
parser.add_option('--registry-host',
|
||||
dest="registry_host", metavar="ADDRESS",
|
||||
default="0.0.0.0",
|
||||
help="Address of a Glance Registry server. "
|
||||
"Default: %default")
|
||||
parser.add_option('--registry-port',
|
||||
dest="registry_port", metavar="PORT", type=int,
|
||||
default=9191,
|
||||
help="Port a Glance Registry server listens on. "
|
||||
"Default: %default")
|
||||
|
||||
glance.store.add_options(parser)
|
||||
config.add_common_options(parser)
|
||||
config.add_daemon_options(parser)
|
||||
config.add_log_options('glance-api', parser)
|
||||
|
||||
|
||||
def main(_args):
|
||||
# NOTE(sirp): importing in main so that eventlet is imported AFTER
|
||||
# daemonization. See https://bugs.launchpad.net/bugs/687661
|
||||
from glance.common import wsgi
|
||||
from glance.server import API
|
||||
server = wsgi.Server()
|
||||
server.start(API(options), options['port'], host=options['host'])
|
||||
server.wait()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
oparser = optparse.OptionParser(version='%%prog %s'
|
||||
% version.version_string())
|
||||
create_options(oparser)
|
||||
conf_options = config.get_config_file_options()
|
||||
(options, args) = config.parse_options(oparser, defaults=conf_options)
|
||||
(options, args) = config.parse_options(oparser)
|
||||
|
||||
try:
|
||||
config.setup_logging(options)
|
||||
server.serve('glance-api', main, options, args)
|
||||
conf, app = config.load_paste_app('glance-api', options, args)
|
||||
|
||||
server = wsgi.Server()
|
||||
server.start(app, int(conf['bind_port']), conf['bind_host'])
|
||||
server.wait()
|
||||
except RuntimeError, e:
|
||||
sys.exit("ERROR: %s" % e)
|
||||
|
||||
@@ -34,9 +34,7 @@ sys.path.append(ROOT_DIR)
|
||||
|
||||
from glance import version
|
||||
from glance.common import config
|
||||
from glance.common import server
|
||||
import glance.registry.db
|
||||
import glance.store
|
||||
from glance.common import wsgi
|
||||
|
||||
|
||||
def create_options(parser):
|
||||
@@ -46,56 +44,24 @@ def create_options(parser):
|
||||
|
||||
:param parser: The option parser
|
||||
"""
|
||||
parser.add_option('--api-host',
|
||||
dest="api_host", metavar="ADDRESS",
|
||||
default="0.0.0.0",
|
||||
help="Address of Glance API server. "
|
||||
"Default: %default")
|
||||
parser.add_option('--api-port',
|
||||
dest="api_port", metavar="PORT", type=int,
|
||||
default=9292,
|
||||
help="Port the Glance API server listens on. "
|
||||
"Default: %default")
|
||||
parser.add_option('--registry-host',
|
||||
dest="registry_host", metavar="ADDRESS",
|
||||
default="0.0.0.0",
|
||||
help="Address of a Glance Registry server. "
|
||||
"Default: %default")
|
||||
parser.add_option('--registry-port',
|
||||
dest="registry_port", metavar="PORT", type=int,
|
||||
default=9191,
|
||||
help="Port a Glance Registry server listens on. "
|
||||
"Default: %default")
|
||||
|
||||
glance.store.add_options(parser)
|
||||
glance.registry.db.add_options(parser)
|
||||
config.add_common_options(parser)
|
||||
config.add_daemon_options(parser)
|
||||
config.add_log_options('glance-combined', parser)
|
||||
|
||||
|
||||
def main(_args):
|
||||
# NOTE(sirp): importing in main so that eventlet is imported AFTER
|
||||
# daemonization. See https://bugs.launchpad.net/bugs/687661
|
||||
from glance.common import wsgi
|
||||
from glance.server import API
|
||||
from glance.registry.server import API as rAPI
|
||||
server = wsgi.Server()
|
||||
server.start(API(options), options['api_port'], host=options['api_host'])
|
||||
server.start(rAPI(options), options['registry_port'],
|
||||
host=options['registry_host'])
|
||||
server.wait()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
oparser = optparse.OptionParser(version='%%prog %s'
|
||||
% version.version_string())
|
||||
create_options(oparser)
|
||||
conf_options = config.get_config_file_options()
|
||||
(options, args) = config.parse_options(oparser, defaults=conf_options)
|
||||
(options, args) = config.parse_options(oparser)
|
||||
|
||||
try:
|
||||
config.setup_logging(options)
|
||||
server.serve('glance-combined', main, options, args)
|
||||
|
||||
server = wsgi.Server()
|
||||
conf, app = config.load_paste_app('glance-api', options, args)
|
||||
server.start(app, int(conf['bind_port']), conf['bind_host'])
|
||||
conf, app = config.load_paste_app('glance-registry', options, args)
|
||||
server.start(app, int(conf['bind_port']), conf['bind_host'])
|
||||
server.wait()
|
||||
except RuntimeError, e:
|
||||
sys.exit("ERROR: %s" % e)
|
||||
|
||||
216
bin/glance-control
Executable file
216
bin/glance-control
Executable file
@@ -0,0 +1,216 @@
|
||||
#!/usr/bin/python
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
# Copyright (c) 2011 OpenStack, LLC.
|
||||
#
|
||||
# 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.
|
||||
|
||||
"""
|
||||
Helper script for starting/stopping/reloading Glance server programs.
|
||||
Thanks for some of the code, Swifties ;)
|
||||
"""
|
||||
|
||||
from __future__ import with_statement
|
||||
|
||||
import errno
|
||||
import glob
|
||||
import os
|
||||
import optparse
|
||||
import resource
|
||||
import signal
|
||||
import sys
|
||||
import time
|
||||
|
||||
from glance import version
|
||||
from glance.common import config
|
||||
|
||||
ALL_COMMANDS = ['start', 'stop', 'shutdown', 'restart',
|
||||
'reload', 'force-reload']
|
||||
ALL_SERVERS = ['glance-api', 'glance-registry']
|
||||
GRACEFUL_SHUTDOWN_SERVERS = ['glance-api', 'glance-registry']
|
||||
MAX_DESCRIPTORS = 32768
|
||||
MAX_MEMORY = (1024 * 1024 * 1024) * 2 # 2 GB
|
||||
USAGE = """%prog [options] <SERVER> <COMMAND> [CONFPATH]
|
||||
|
||||
Where <SERVER> is one of:
|
||||
|
||||
all, api, registry
|
||||
|
||||
And command is one of:
|
||||
|
||||
start, stop, shutdown, restart, reload, force-reload
|
||||
|
||||
And CONFPATH is the optional configuration file to use."""
|
||||
|
||||
|
||||
def pid_files(server):
|
||||
if os.path.exists('/var/run/glance/%s.pid' % server):
|
||||
pid_files = ['/var/run/glance/%s.pid' % server]
|
||||
else:
|
||||
pid_files = glob.glob('/var/run/glance/%s/*.pid' % server)
|
||||
for pid_file in pid_files:
|
||||
pid = int(open(pid_file).read().strip())
|
||||
yield pid_file, pid
|
||||
|
||||
|
||||
def do_start(server, options, args):
|
||||
server_type = '-'.join(server.split('-')[:-1])
|
||||
|
||||
for pid_file, pid in pid_files(server):
|
||||
if os.path.exists('/proc/%s' % pid):
|
||||
print "%s appears to already be running: %s" % (server, pid_file)
|
||||
return
|
||||
else:
|
||||
print "Removing stale pid file %s" % pid_file
|
||||
os.unlink(pid_file)
|
||||
|
||||
try:
|
||||
resource.setrlimit(resource.RLIMIT_NOFILE,
|
||||
(MAX_DESCRIPTORS, MAX_DESCRIPTORS))
|
||||
resource.setrlimit(resource.RLIMIT_DATA,
|
||||
(MAX_MEMORY, MAX_MEMORY))
|
||||
except ValueError:
|
||||
print "Unable to increase file descriptor limit. Running as non-root?"
|
||||
os.environ['PYTHON_EGG_CACHE'] = '/tmp'
|
||||
|
||||
def write_pid_file(pid_file, pid):
|
||||
dir, file = os.path.split(pid_file)
|
||||
if not os.path.exists(dir):
|
||||
try:
|
||||
os.makedirs(dir)
|
||||
except OSError, err:
|
||||
if err.errno == errno.EACCES:
|
||||
sys.exit('Unable to create %s. Running as non-root?'
|
||||
% dir)
|
||||
fp = open(pid_file, 'w')
|
||||
fp.write('%d\n' % pid)
|
||||
fp.close()
|
||||
|
||||
def launch(ini_file, pid_file):
|
||||
args = [server, ini_file]
|
||||
print 'Starting %s with %s' % (server, ini_file)
|
||||
|
||||
pid = os.fork()
|
||||
if pid == 0:
|
||||
os.setsid()
|
||||
with open(os.devnull, 'r+b') as nullfile:
|
||||
for desc in (0, 1, 2): # close stdio
|
||||
try:
|
||||
os.dup2(nullfile.fileno(), desc)
|
||||
except OSError:
|
||||
pass
|
||||
try:
|
||||
os.execlp('%s' % server, server, ini_file)
|
||||
except OSError, e:
|
||||
sys.exit('unable to launch %s. Got error: %s'
|
||||
% (server, str(e)))
|
||||
sys.exit(0)
|
||||
else:
|
||||
write_pid_file(pid_file, pid)
|
||||
|
||||
pid_file = '/var/run/glance/%s.pid' % server
|
||||
conf_file = config.find_config_file(options, args)
|
||||
if not conf_file:
|
||||
sys.exit("Could not find any configuration file to use!")
|
||||
launch_args = [(conf_file, pid_file)]
|
||||
|
||||
# start all servers
|
||||
for conf_file, pid_file in launch_args:
|
||||
launch(conf_file, pid_file)
|
||||
|
||||
|
||||
def do_stop(server, options, args, graceful=False):
|
||||
if graceful and server in GRACEFUL_SHUTDOWN_SERVERS:
|
||||
sig = signal.SIGHUP
|
||||
else:
|
||||
sig = signal.SIGTERM
|
||||
|
||||
did_anything = False
|
||||
pfiles = pid_files(server)
|
||||
for pid_file, pid in pfiles:
|
||||
did_anything = True
|
||||
try:
|
||||
print 'Stopping %s pid: %s signal: %s' % (server, pid, sig)
|
||||
os.kill(pid, sig)
|
||||
except OSError:
|
||||
print "Process %d not running" % pid
|
||||
try:
|
||||
os.unlink(pid_file)
|
||||
except OSError:
|
||||
pass
|
||||
for pid_file, pid in pfiles:
|
||||
for _junk in xrange(150): # 15 seconds
|
||||
if not os.path.exists('/proc/%s' % pid):
|
||||
break
|
||||
time.sleep(0.1)
|
||||
else:
|
||||
print 'Waited 15 seconds for pid %s (%s) to die; giving up' % \
|
||||
(pid, pid_file)
|
||||
if not did_anything:
|
||||
print 'No %s running' % server
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
oparser = optparse.OptionParser(usage=USAGE, version='%%prog %s'
|
||||
% version.version_string())
|
||||
config.add_common_options(oparser)
|
||||
(options, args) = config.parse_options(oparser)
|
||||
|
||||
if len(args) < 2:
|
||||
oparser.print_usage()
|
||||
sys.exit(1)
|
||||
|
||||
server = args.pop(0).lower()
|
||||
if server == 'all':
|
||||
servers = ALL_SERVERS
|
||||
else:
|
||||
if not server.startswith('glance-'):
|
||||
server = 'glance-%s' % server
|
||||
if server not in ALL_SERVERS:
|
||||
server_list = ", ".join([s.replace('glance-', '')
|
||||
for s in ALL_SERVERS])
|
||||
msg = ("Unknown server '%(server)s' specified. Please specify "
|
||||
"all, or one of the servers: %(server_list)s" % locals())
|
||||
sys.exit(msg)
|
||||
servers = [server]
|
||||
|
||||
command = args.pop(0).lower()
|
||||
if command not in ALL_COMMANDS:
|
||||
command_list = ", ".join(ALL_COMMANDS)
|
||||
msg = ("Unknown command %(command)s specified. Please specify a "
|
||||
"command in this list: %(command_list)s" % locals())
|
||||
sys.exit(msg)
|
||||
|
||||
if command == 'start':
|
||||
for server in servers:
|
||||
do_start(server, options, args)
|
||||
|
||||
if command == 'stop':
|
||||
for server in servers:
|
||||
do_stop(server, options, args)
|
||||
|
||||
if command == 'shutdown':
|
||||
for server in servers:
|
||||
do_stop(server, options, args, graceful=True)
|
||||
|
||||
if command == 'restart':
|
||||
for server in servers:
|
||||
do_stop(server, options, args)
|
||||
for server in servers:
|
||||
do_start(server, options, args)
|
||||
|
||||
if command == 'reload' or command == 'force-reload':
|
||||
for server in servers:
|
||||
do_stop(server, options, args, graceful=True)
|
||||
do_start(server, options, args)
|
||||
@@ -30,10 +30,9 @@ ROOT_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
||||
|
||||
sys.path.append(ROOT_DIR)
|
||||
|
||||
import glance.registry.db
|
||||
from glance import version
|
||||
from glance.common import config
|
||||
from glance.common import server
|
||||
from glance.common import wsgi
|
||||
|
||||
|
||||
def create_options(parser):
|
||||
@@ -43,42 +42,22 @@ def create_options(parser):
|
||||
|
||||
:param parser: The option parser
|
||||
"""
|
||||
parser.add_option('-H', '--host',
|
||||
dest="host", metavar="ADDRESS",
|
||||
default="0.0.0.0",
|
||||
help="Address of Glance API server. "
|
||||
"Default: %default")
|
||||
parser.add_option('-p', '--port',
|
||||
dest="port", metavar="PORT", type=int,
|
||||
default=9191,
|
||||
help="Port the Glance Registry server listens on. "
|
||||
"Default: %default")
|
||||
|
||||
glance.registry.db.add_options(parser)
|
||||
config.add_common_options(parser)
|
||||
config.add_daemon_options(parser)
|
||||
config.add_log_options('glance-registry', parser)
|
||||
|
||||
|
||||
def main(_args):
|
||||
# NOTE(sirp): importing in main so that eventlet is imported AFTER
|
||||
# daemonization. See https://bugs.launchpad.net/bugs/687661
|
||||
from glance.common import wsgi
|
||||
from glance.registry.server import API
|
||||
server = wsgi.Server()
|
||||
server.start(API(options), int(options['port']), host=options['host'])
|
||||
server.wait()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
oparser = optparse.OptionParser(version='%%prog %s'
|
||||
% version.version_string())
|
||||
create_options(oparser)
|
||||
conf_options = config.get_config_file_options()
|
||||
(options, args) = config.parse_options(oparser, defaults=conf_options)
|
||||
(options, args) = config.parse_options(oparser)
|
||||
|
||||
try:
|
||||
config.setup_logging(options)
|
||||
server.serve('glance-registry', main, options, args)
|
||||
conf, app = config.load_paste_app('glance-registry', options, args)
|
||||
|
||||
server = wsgi.Server()
|
||||
server.start(app, int(conf['bind_port']), conf['bind_host'])
|
||||
server.wait()
|
||||
except RuntimeError, e:
|
||||
sys.exit("ERROR: %s" % e)
|
||||
|
||||
20
doc/source/configuring.rst
Normal file
20
doc/source/configuring.rst
Normal file
@@ -0,0 +1,20 @@
|
||||
..
|
||||
Copyright 2011 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.
|
||||
|
||||
Configuring Glance
|
||||
==================
|
||||
|
||||
.. todo:: Complete details of configuration with paste.deploy config files
|
||||
165
doc/source/controllingservers.rst
Normal file
165
doc/source/controllingservers.rst
Normal file
@@ -0,0 +1,165 @@
|
||||
..
|
||||
Copyright 2011 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.
|
||||
|
||||
Controlling Glance Servers
|
||||
==========================
|
||||
|
||||
This section describes the ways to start, stop, and reload Glance's server
|
||||
programs.
|
||||
|
||||
Starting a server
|
||||
-----------------
|
||||
|
||||
There are two ways to start a Glance server (either the API server or the
|
||||
reference implementation registry server that ships with Glance):
|
||||
|
||||
* Manually calling the server program
|
||||
|
||||
* Using the ``glance-control`` server daemon wrapper program
|
||||
|
||||
Manually starting the server
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
The first is by directly calling the server program, passing in command-line
|
||||
options and a single argument for the ``paste.deploy`` configuration file to
|
||||
use when configuring the server application.
|
||||
|
||||
.. note::
|
||||
|
||||
Glance ships with an ``etc/`` directory that contains sample ``paste.deploy``
|
||||
configuration files that you can copy to a standard configuation directory and
|
||||
adapt for your own uses.
|
||||
|
||||
If you do `not` specifiy a configuration file on the command line, Glance will
|
||||
do its best to locate a ``glance.conf`` configuration file in one of the
|
||||
following directories, stopping at the first config file it finds:
|
||||
|
||||
* .
|
||||
|
||||
* ~/.glance
|
||||
|
||||
* ~/
|
||||
|
||||
* /etc/glance/
|
||||
|
||||
* /etc
|
||||
|
||||
If no configuration file is found, you will see any error, like so::
|
||||
|
||||
$> glance-api
|
||||
ERROR: Unable to locate any configuration file. Cannot load application glance-api
|
||||
|
||||
Here is an example showing how you can manually start the ``glance-api`` server
|
||||
in a shell.::
|
||||
|
||||
$> sudo glance-api etc/glance.conf.sample --debug
|
||||
2011-02-09 14:58:29 DEBUG [glance-api] ********************************************************************************
|
||||
2011-02-09 14:58:29 DEBUG [glance-api] Configuration options gathered from config file:
|
||||
2011-02-09 14:58:29 DEBUG [glance-api] /home/jpipes/repos/glance/trunk/etc/glance.conf.sample
|
||||
2011-02-09 14:58:29 DEBUG [glance-api] ================================================
|
||||
2011-02-09 14:58:29 DEBUG [glance-api] bind_host 0.0.0.0
|
||||
2011-02-09 14:58:29 DEBUG [glance-api] bind_port 9292
|
||||
2011-02-09 14:58:29 DEBUG [glance-api] debug True
|
||||
2011-02-09 14:58:29 DEBUG [glance-api] default_store file
|
||||
2011-02-09 14:58:29 DEBUG [glance-api] filesystem_store_datadir /var/lib/glance/images/
|
||||
2011-02-09 14:58:29 DEBUG [glance-api] registry_host 0.0.0.0
|
||||
2011-02-09 14:58:29 DEBUG [glance-api] registry_port 9191
|
||||
2011-02-09 14:58:29 DEBUG [glance-api] verbose False
|
||||
2011-02-09 14:58:29 DEBUG [glance-api] ********************************************************************************
|
||||
2011-02-09 14:58:29 DEBUG [routes.middleware] Initialized with method overriding = True, and path info altering = True
|
||||
(16333) wsgi starting up on http://0.0.0.0:9292/
|
||||
|
||||
Simply supply the configuration file as the first argument
|
||||
(``etc/glance.conf.sample`` in the above example) and then any common options
|
||||
you want to use (``--debug`` was used above to show some of the debugging
|
||||
output that the server shows when starting up. Call the server program
|
||||
with ``--help`` to see all available options you can specify on the
|
||||
command line.)
|
||||
|
||||
For more information on configuring the server via the ``paste.deploy``
|
||||
configuration files, see the section entitled
|
||||
:doc:`Configuring Glance servers <configuring>`
|
||||
|
||||
Note that the server does not `daemonize` itself when run manually
|
||||
from the terminal. You can force the server to daemonize using the standard
|
||||
shell backgrounding indicator, ``&``. However, for most use cases, we recommend
|
||||
using the ``glance-control`` server daemon wrapper for daemonizing. See below
|
||||
for more details on daemonization with ``glance-control``.
|
||||
|
||||
Using the ``glance-control`` program to start the server
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
The second way to start up a Glance server is to use the ``glance-control``
|
||||
program. ``glance-control`` is a wrapper script that allows the user to
|
||||
start, stop, restart, and reload the other Glance server programs in
|
||||
a fashion that is more conducive to automation and scripting.
|
||||
|
||||
Servers started via the ``glance-control`` program are always `daemonized`,
|
||||
meaning that the server program process runs in the background.
|
||||
|
||||
To start a Glance server with ``glance-control``, simply call
|
||||
``glance-control`` with a server and the word "start", followed by
|
||||
any command-line options you wish to provide. Start the server with ``glance-control``
|
||||
in the following way::
|
||||
|
||||
$> sudo glance-control <SERVER> start [CONFPATH]
|
||||
|
||||
.. note::
|
||||
|
||||
You must use the ``sudo`` program to run ``glance-control`` currently, as the
|
||||
pid files for the server programs are written to /var/run/glance/
|
||||
|
||||
Here is an example that shows how to start the ``glance-registry`` server
|
||||
with the ``glance-control`` wrapper script. ::
|
||||
|
||||
$> sudo glance-control registry start etc/glance.conf.sample
|
||||
Starting glance-registry with /home/jpipes/repos/glance/trunk/etc/glance.conf.sample
|
||||
|
||||
The same ``paste.deploy`` configuration files are used by ``glance-control``
|
||||
to start the Glance server programs, and you can specify (as the example above
|
||||
shows) a configuration file when starting the server.
|
||||
|
||||
.. note::
|
||||
|
||||
To start all the Glance servers (currently the glance-api and glance-registry
|
||||
programs) at once, you can specify "all" for the <SERVER>
|
||||
|
||||
Stopping a server
|
||||
-----------------
|
||||
|
||||
If you started a Glance server manually and did not use the ``&`` backgrounding
|
||||
function, simply send a terminate signal to the server process by typing
|
||||
``Ctrl-C``
|
||||
|
||||
If you started the Glance server using the ``glance-control`` program, you can
|
||||
use the ``glance-control`` program to stop it. Simply do the following::
|
||||
|
||||
$> sudo glance-control <SERVER> stop
|
||||
|
||||
as this example shows::
|
||||
|
||||
$> sudo glance-control registry stop
|
||||
Stopping glance-registry pid: 17602 signal: 15
|
||||
|
||||
Restarting a server
|
||||
-------------------
|
||||
|
||||
You can restart a server with the ``glance-control`` program, as demonstrated
|
||||
here::
|
||||
|
||||
$> sudo glance-control registry restart etc/glance.conf.sample
|
||||
Stopping glance-registry pid: 17611 signal: 15
|
||||
Starting glance-registry with /home/jpipes/repos/glance/trunk/etc/glance.conf.sample
|
||||
@@ -76,48 +76,6 @@ Glance Registry Servers
|
||||
|
||||
Glance registry servers are servers that conform to the Glance Registry API.
|
||||
Glance ships with a reference implementation of a registry server that
|
||||
complies with this API (``bin/glance-registry``).
|
||||
complies with this API (``glance-registry``).
|
||||
|
||||
|
||||
Starting Up Glance's Servers
|
||||
----------------------------
|
||||
|
||||
To get started using Glance, you must first start the Glance API server.
|
||||
After installing Glance, starting up the Glance API server is easy. Simply
|
||||
start the ``glance-api`` program, like so::
|
||||
|
||||
$> glance-api
|
||||
|
||||
Configuring the Glance API server
|
||||
*********************************
|
||||
|
||||
There are a few options that can be supplied to the API server when
|
||||
starting it up:
|
||||
|
||||
* ``verbose``
|
||||
|
||||
Show more verbose/debugging output
|
||||
|
||||
* ``api_host``
|
||||
|
||||
Address of the host the registry runs on. Defaults to 0.0.0.0.
|
||||
|
||||
* ``api_port``
|
||||
|
||||
Port the registry server listens on. Defaults to 9292.
|
||||
|
||||
* ``default_store``
|
||||
|
||||
The store that the Glance API server will use by default to store
|
||||
images that are added to it. The default value is `filesystem`, and
|
||||
possible choices are: `filesystem`, `swift`, and `s3`.
|
||||
|
||||
* ``filesystem_store_datadir``
|
||||
|
||||
Directory where the filesystem store can write images to. This directory
|
||||
must be writeable by the user that runs ``glance-api``
|
||||
|
||||
.. todo::
|
||||
|
||||
Link to docs on the different stores when the documentation on Glance
|
||||
stores is complete.
|
||||
For more details on Glance's architecture see :doc:`here <architecture>`
|
||||
|
||||
@@ -58,6 +58,9 @@ Using Glance
|
||||
:maxdepth: 1
|
||||
|
||||
gettingstarted
|
||||
installing
|
||||
controllingservers
|
||||
configuring
|
||||
glanceapi
|
||||
client
|
||||
|
||||
|
||||
100
doc/source/installing.rst
Normal file
100
doc/source/installing.rst
Normal file
@@ -0,0 +1,100 @@
|
||||
..
|
||||
Copyright 2011 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.
|
||||
|
||||
Installing Glance
|
||||
=================
|
||||
|
||||
Installing from packages
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
To install the latest version of Glance from the Launchpad Bazaar repositories,
|
||||
following the following instructions.
|
||||
|
||||
Debian/Ubuntu
|
||||
#############
|
||||
|
||||
1. Add the Glance PPA to your sources.lst::
|
||||
|
||||
$> sudo add-apt-repository ppa:glance-core/trunk
|
||||
$> sudo apt-get update
|
||||
|
||||
2. Install Glance::
|
||||
|
||||
$> sudo apt-get install glance
|
||||
|
||||
RedHat/Fedora
|
||||
#############
|
||||
|
||||
.. todo:: Need some help on this one...
|
||||
|
||||
Mac OSX
|
||||
#######
|
||||
|
||||
.. todo:: No idea how to do install on Mac OSX. Somebody with a Mac should complete this section
|
||||
|
||||
Installing from source tarballs
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
To install the latest version of Glance from the Launchpad Bazaar repositories,
|
||||
following the following instructions.
|
||||
|
||||
1. Grab the source tarball from `Launchpad <http://launchpad.net/glance/+download>`_
|
||||
|
||||
2. Untar the source tarball::
|
||||
|
||||
$> tar -xzf <FILE>
|
||||
|
||||
3. Change into the package directory and build/install::
|
||||
|
||||
$> cd glance-<RELEASE>
|
||||
$> sudo python setup.py install
|
||||
|
||||
Installing from a Bazaar Branch
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
To install the latest version of Glance from the Launchpad Bazaar repositories,
|
||||
following the following instructions.
|
||||
|
||||
Debian/Ubuntu
|
||||
#############
|
||||
|
||||
1. Install Bazaar and build dependencies::
|
||||
|
||||
$> sudo apt-get install bzr python-eventlet python-routes python-greenlet
|
||||
$> sudo apt-get install python-argparse python-sqlalchemy python-wsgiref python-pastedeploy
|
||||
|
||||
.. note::
|
||||
|
||||
If you want to build the Glance documentation locally, you will also want
|
||||
to install the python-sphinx package
|
||||
|
||||
1. Branch Glance's trunk branch::
|
||||
|
||||
$> bzr branch lp:glance
|
||||
|
||||
1. Install Glance::
|
||||
|
||||
$> sudo python setup.py install
|
||||
|
||||
RedHat/Fedora
|
||||
#############
|
||||
|
||||
.. todo:: Need some help on this one...
|
||||
|
||||
Mac OSX
|
||||
#######
|
||||
|
||||
.. todo:: No idea how to do install on Mac OSX. Somebody with a Mac should complete this section
|
||||
@@ -1,20 +0,0 @@
|
||||
[DEFAULT]
|
||||
# Show more verbose log output (sets INFO log level output)
|
||||
# verbose = True
|
||||
|
||||
# Show debugging output in logs (sets DEBUG log level output)
|
||||
# debug = True
|
||||
|
||||
# Which backend store should Glance use by default is not specified
|
||||
# in a request to add a new image to Glance? Default: 'file'
|
||||
# Available choices are 'file', 'swift', and 's3'
|
||||
# default_store = file
|
||||
|
||||
# SQLAlchemy connection string for the reference implementation
|
||||
# registry server. Any valid SQLAlchemy connection string is fine.
|
||||
# See: http://www.sqlalchemy.org/docs/05/reference/sqlalchemy/connections.html#sqlalchemy.create_engine
|
||||
# sql_connection = sqlite://glance.sqlite
|
||||
|
||||
# The directory that the Filesystem backend store will write disk
|
||||
# images to. Default: /var/lib/glance/images
|
||||
# filesystem_store_datadir = /var/lib/glance/images
|
||||
44
etc/glance.conf.sample
Normal file
44
etc/glance.conf.sample
Normal file
@@ -0,0 +1,44 @@
|
||||
[DEFAULT]
|
||||
# Show more verbose log output (sets INFO log level output)
|
||||
verbose = True
|
||||
|
||||
# Show debugging output in logs (sets DEBUG log level output)
|
||||
debug = False
|
||||
|
||||
[app:glance-api]
|
||||
paste.app_factory = glance.server:app_factory
|
||||
|
||||
# Directory that the Filesystem backend store
|
||||
# writes image data to
|
||||
filesystem_store_datadir=/var/lib/glance/images/
|
||||
|
||||
# Which backend store should Glance use by default is not specified
|
||||
# in a request to add a new image to Glance? Default: 'file'
|
||||
# Available choices are 'file', 'swift', and 's3'
|
||||
default_store = file
|
||||
|
||||
# Address to bind the API server
|
||||
bind_host = 0.0.0.0
|
||||
|
||||
# Port the bind the API server to
|
||||
bind_port = 9292
|
||||
|
||||
# Address to find the registry server
|
||||
registry_host = 0.0.0.0
|
||||
|
||||
# Port the registry server is listening on
|
||||
registry_port = 9191
|
||||
|
||||
[app:glance-registry]
|
||||
paste.app_factory = glance.registry.server:app_factory
|
||||
|
||||
# Address to bind the registry server
|
||||
bind_host = 0.0.0.0
|
||||
|
||||
# Port the bind the registry server to
|
||||
bind_port = 9191
|
||||
|
||||
# SQLAlchemy connection string for the reference implementation
|
||||
# registry server. Any valid SQLAlchemy connection string is fine.
|
||||
# See: http://www.sqlalchemy.org/docs/05/reference/sqlalchemy/connections.html#sqlalchemy.create_engine
|
||||
sql_connection = sqlite:///glance.sqlite
|
||||
@@ -29,6 +29,8 @@ import os
|
||||
import re
|
||||
import sys
|
||||
|
||||
from paste import deploy
|
||||
|
||||
import glance.common.exception as exception
|
||||
|
||||
DEFAULT_LOG_FORMAT = "%(asctime)s %(levelname)8s [%(name)s] %(message)s"
|
||||
@@ -37,7 +39,7 @@ DEFAULT_LOG_HANDLER = 'stream'
|
||||
LOGGING_HANDLER_CHOICES = ['syslog', 'file', 'stream']
|
||||
|
||||
|
||||
def parse_options(parser, cli_args=None, defaults=None):
|
||||
def parse_options(parser, cli_args=None):
|
||||
"""
|
||||
Returns the parsed CLI options, command to run and its arguments, merged
|
||||
with any same-named options found in a configuration file.
|
||||
@@ -52,47 +54,14 @@ def parse_options(parser, cli_args=None, defaults=None):
|
||||
:param parser: The option parser
|
||||
:param cli_args: (Optional) Set of arguments to process. If not present,
|
||||
sys.argv[1:] is used.
|
||||
:param defaults: (optional) mapping of default values for options
|
||||
:retval tuple of (options, args)
|
||||
"""
|
||||
|
||||
if defaults:
|
||||
int_re = re.compile(r'^\d+$')
|
||||
float_re = re.compile(r'^([+-]?(((\d+(\.)?)|(\d*\.\d+))'
|
||||
'([eE][+-]?\d+)?))$')
|
||||
for key, value in defaults.items():
|
||||
# Do our best to figure out what the actual option
|
||||
# type is underneath...
|
||||
if value.lower() in ('true', 'on'):
|
||||
value = True
|
||||
elif value.lower() in ('false', 'off'):
|
||||
value = False
|
||||
elif int_re.match(value):
|
||||
value = int(value)
|
||||
elif float_re.match(value):
|
||||
value = float(value)
|
||||
defaults[key] = value
|
||||
|
||||
parser.set_defaults(**defaults)
|
||||
(options, args) = parser.parse_args(cli_args)
|
||||
|
||||
return (vars(options), args)
|
||||
|
||||
|
||||
def options_to_conf(options):
|
||||
"""
|
||||
Converts a mapping of options having typed values into
|
||||
a mapping of configuration options having only stringified values.
|
||||
|
||||
This method is used to convert the return of parse_options()[0]
|
||||
into the configuration mapping that is expected by ConfigParser
|
||||
and paste.deploy.
|
||||
|
||||
:params options: Mapping of typed option key/values
|
||||
"""
|
||||
return dict([(k, str(v)) for k, v in options.items()])
|
||||
|
||||
|
||||
def add_common_options(parser):
|
||||
"""
|
||||
Given a supplied optparse.OptionParser, adds an OptionGroup that
|
||||
@@ -124,10 +93,16 @@ def add_daemon_options(parser):
|
||||
"the daemonizing of this program."
|
||||
|
||||
group = optparse.OptionGroup(parser, "Daemon Options", help_text)
|
||||
group.add_option('--daemonize', default=False, action="store_true",
|
||||
help="Daemonize this process")
|
||||
group.add_option("--pidfile", default=None,
|
||||
help="(Optional) Name of pid file for the server")
|
||||
group.add_option('--config', default=None,
|
||||
help="Configuration file to read when loading "
|
||||
"application. If missing, the first argument is "
|
||||
"used. If no arguments are found, then a set of "
|
||||
"standard directories are searched for a config "
|
||||
"file.")
|
||||
group.add_option("--pid-file", default=None, metavar="PATH",
|
||||
help="(Optional) Name of pid file for the server. "
|
||||
"If not specified, the pid file will be named "
|
||||
"/var/run/glance/<SERVER>.pid.")
|
||||
group.add_option("--uid", type=int, default=os.getuid(),
|
||||
help="uid under which to run. Default: %default")
|
||||
group.add_option("--gid", type=int, default=os.getgid(),
|
||||
@@ -160,9 +135,6 @@ def add_log_options(prog_name, parser):
|
||||
choices=LOGGING_HANDLER_CHOICES,
|
||||
help="What logging handler to use? "
|
||||
"Default: %default")
|
||||
group.add_option('--log-format', metavar="FORMAT",
|
||||
default=DEFAULT_LOG_FORMAT,
|
||||
help="Format string for log records. Default: %default")
|
||||
group.add_option('--log-date-format', metavar="FORMAT",
|
||||
default=DEFAULT_LOG_DATE_FORMAT,
|
||||
help="Format string for %(asctime)s in log records. "
|
||||
@@ -203,6 +175,9 @@ def setup_logging(options):
|
||||
root_logger.setLevel(logging.WARNING)
|
||||
|
||||
# Set log configuration from options...
|
||||
# Note that we use a hard-coded log format in the options
|
||||
# because of Paste.Deploy bug #379
|
||||
# http://trac.pythonpaste.org/pythonpaste/ticket/379
|
||||
log_format = options.get('log_format', DEFAULT_LOG_FORMAT)
|
||||
log_date_format = options.get('log_date_format', DEFAULT_LOG_DATE_FORMAT)
|
||||
formatter = logging.Formatter(log_format, log_date_format)
|
||||
@@ -229,79 +204,92 @@ def setup_logging(options):
|
||||
raise exception.BadInputError(
|
||||
"unrecognized log handler '%(log_handler)s'" % locals())
|
||||
|
||||
# Log the options used when starting if we're in debug mode...
|
||||
if debug:
|
||||
root_logger.debug("*" * 80)
|
||||
root_logger.debug("Options:")
|
||||
root_logger.debug("========")
|
||||
for key, value in sorted(options.items()):
|
||||
root_logger.debug("%(key)-30s %(value)s" % locals())
|
||||
root_logger.debug("*" * 80)
|
||||
|
||||
|
||||
def get_config_file_options(conf_file=None, conf_dirs=None, app_name=None):
|
||||
def find_config_file(options, args):
|
||||
"""
|
||||
Look for configuration files in a number of standard directories and
|
||||
return a mapping of options found in the files.
|
||||
Return the first config file found.
|
||||
|
||||
The files that are searched for are in the following order, with
|
||||
options found in later files overriding options found in earlier
|
||||
files::
|
||||
We search for the paste config file in the following order:
|
||||
* If --config-file option is used, use that
|
||||
* If args[0] is a file, use that
|
||||
* Search for glance.conf in standard directories:
|
||||
* .
|
||||
* ~.glance/
|
||||
* ~
|
||||
* /etc/glance
|
||||
* /etc
|
||||
|
||||
/etc/glance.cnf
|
||||
/etc/glance/glance.cnf
|
||||
~/glance.cnf
|
||||
~/.glance/glance.cnf
|
||||
./glance.cnf
|
||||
supplied conf_file param, if any.
|
||||
|
||||
:param conf_file: (optional) config file to read options from. Options
|
||||
from this config file override all others
|
||||
:param conf_dirs: (optional) sequence of directory paths to search for
|
||||
config files. Generally just used in testing
|
||||
:param app_name: (optional) name of application we're interested in.
|
||||
Supplying this will ensure that only the [DEFAULT]
|
||||
section and the [app_name] sections of the config
|
||||
files will be read. If not supplied (the default), all
|
||||
sections are read for configuration options.
|
||||
|
||||
:retval Mapping of configuration options read from config files
|
||||
:retval Full path to config file, or None if no config file found
|
||||
"""
|
||||
|
||||
# Note that we do this in reverse priority order because
|
||||
# later configs overwrite the values of previously-read
|
||||
# configuration options
|
||||
fix_path = lambda p: os.path.abspath(os.path.expanduser(p))
|
||||
if getattr(options, 'config', None):
|
||||
if os.path.exists(options.config_file):
|
||||
return fix_path(getattr(options, 'config'))
|
||||
elif args:
|
||||
if os.path.exists(args[0]):
|
||||
return fix_path(args[0])
|
||||
|
||||
fixup_path = lambda p: os.path.abspath(os.path.expanduser(p))
|
||||
config_file_dirs = conf_dirs or \
|
||||
['/etc',
|
||||
# Handle standard directory search for glance.conf
|
||||
config_file_dirs = [fix_path(os.getcwd()),
|
||||
fix_path(os.path.join('~', '.glance')),
|
||||
fix_path('~'),
|
||||
'/etc/glance/',
|
||||
fixup_path('~'),
|
||||
fixup_path(os.path.join('~', '.glance')),
|
||||
fixup_path(os.getcwd())]
|
||||
'/etc']
|
||||
|
||||
config_files = []
|
||||
results = {}
|
||||
for cfg_dir in config_file_dirs:
|
||||
cfg_file = os.path.join(cfg_dir, 'glance.cnf')
|
||||
cfg_file = os.path.join(cfg_dir, 'glance.conf')
|
||||
if os.path.exists(cfg_file):
|
||||
config_files.append(cfg_file)
|
||||
return cfg_file
|
||||
|
||||
if conf_file:
|
||||
config_files.append(fixup_path(conf_file))
|
||||
|
||||
cp = ConfigParser.ConfigParser()
|
||||
for config_file in config_files:
|
||||
if not cp.read(config_file):
|
||||
msg = 'Unable to read config file: %s' % config_file
|
||||
raise RuntimeError(msg)
|
||||
def load_paste_app(app_name, options, args):
|
||||
"""
|
||||
Builds and returns a WSGI app from a paste config file.
|
||||
|
||||
results.update(cp.defaults())
|
||||
# Add any sections we have in the configuration file, too...
|
||||
for section in cp.sections():
|
||||
section_option_keys = cp.options(section)
|
||||
if not app_name or (app_name == section):
|
||||
for k in section_option_keys:
|
||||
results[k] = cp.get(section, k)
|
||||
We search for the paste config file in the following order:
|
||||
* If --config-file option is used, use that
|
||||
* If args[0] is a file, use that
|
||||
* Search for glance.conf in standard directories:
|
||||
* .
|
||||
* ~.glance/
|
||||
* ~
|
||||
* /etc/glance
|
||||
* /etc
|
||||
|
||||
return results
|
||||
:param app_name: Name of the application to load
|
||||
:param options: Set of typed options returned from parse_options()
|
||||
:param args: Command line arguments from argv[1:]
|
||||
|
||||
:raises RuntimeError when config file cannot be located or application
|
||||
cannot be loaded from config file
|
||||
"""
|
||||
conf_file = find_config_file(options, args)
|
||||
if not conf_file:
|
||||
raise RuntimeError("Unable to locate any configuration file. "
|
||||
"Cannot load application %s" % app_name)
|
||||
try:
|
||||
conf = deploy.appconfig("config:%s" % conf_file, name=app_name)
|
||||
# We only update the conf dict for the verbose and debug
|
||||
# flags. Everything else must be set up in the conf file...
|
||||
conf['verbose'] = options['verbose']
|
||||
conf['debug'] = options['debug']
|
||||
|
||||
# Log the options used when starting if we're in debug mode...
|
||||
if conf['debug']:
|
||||
logger = logging.getLogger(app_name)
|
||||
logger.debug("*" * 80)
|
||||
logger.debug("Configuration options gathered from config file:")
|
||||
logger.debug(conf_file)
|
||||
logger.debug("================================================")
|
||||
items = dict([(k, v) for k, v in conf.items()
|
||||
if k not in ('__file__', 'here')])
|
||||
for key, value in sorted(items.items()):
|
||||
logger.debug("%(key)-30s %(value)s" % locals())
|
||||
logger.debug("*" * 80)
|
||||
app = deploy.loadapp("config:%s" % conf_file, name=app_name)
|
||||
except (LookupError, ImportError), e:
|
||||
raise RuntimeError("Unable to load %(app_name)s from "
|
||||
"configuration file %(conf_file)s."
|
||||
"\nGot: %(e)r" % locals())
|
||||
return conf, app
|
||||
|
||||
@@ -1,129 +0,0 @@
|
||||
# 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.
|
||||
|
||||
"""
|
||||
Base functionality for nova daemons - gradually being replaced with twistd.py.
|
||||
"""
|
||||
|
||||
import daemon
|
||||
from daemon import pidlockfile
|
||||
import logging
|
||||
import logging.handlers
|
||||
import os
|
||||
import pprint
|
||||
import signal
|
||||
import sys
|
||||
import time
|
||||
|
||||
|
||||
def stop(pidfile):
|
||||
"""
|
||||
Stop the daemon
|
||||
"""
|
||||
# Get the pid from the pidfile
|
||||
try:
|
||||
pid = int(open(pidfile, 'r').read().strip())
|
||||
except IOError:
|
||||
message = "pidfile %s does not exist. Daemon not running?\n"
|
||||
sys.stderr.write(message % pidfile)
|
||||
return
|
||||
|
||||
# Try killing the daemon process
|
||||
try:
|
||||
print "Killing process from pidfile %s" % pidfile
|
||||
while 1:
|
||||
os.kill(pid, signal.SIGTERM)
|
||||
time.sleep(0.1)
|
||||
except OSError, err:
|
||||
err = str(err)
|
||||
if err.find("No such process") > 0:
|
||||
if os.path.exists(pidfile):
|
||||
os.remove(pidfile)
|
||||
else:
|
||||
print str(err)
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
def serve(name, main, options, args):
|
||||
"""Controller for server"""
|
||||
|
||||
pidfile = options['pidfile']
|
||||
if not pidfile:
|
||||
options['pidfile'] = '%s.pid' % name
|
||||
|
||||
action = 'start'
|
||||
if len(args):
|
||||
action = args.pop()
|
||||
|
||||
if action == 'stop':
|
||||
stop(options['pidfile'])
|
||||
sys.exit()
|
||||
elif action == 'restart':
|
||||
stop(options['pidfile'])
|
||||
elif action == 'start':
|
||||
pass
|
||||
else:
|
||||
print 'usage: %s [options] [start|stop|restart]' % name
|
||||
sys.exit(1)
|
||||
daemonize(args, name, main, options)
|
||||
|
||||
|
||||
def daemonize(args, name, main, options):
|
||||
"""Does the work of daemonizing the process"""
|
||||
logging.getLogger('amqplib').setLevel(logging.WARN)
|
||||
pidfile = options['pidfile']
|
||||
logfile = options['log_file']
|
||||
logdir = options['log_dir']
|
||||
daemonize = options['daemonize']
|
||||
use_syslog = options['log_handler'] == 'syslog'
|
||||
files_to_keep = []
|
||||
if daemonize:
|
||||
logger = logging.getLogger(name)
|
||||
formatter = logging.Formatter(
|
||||
name + '(%(name)s): %(levelname)s %(message)s')
|
||||
if use_syslog and not logfile:
|
||||
syslog = logging.handlers.SysLogHandler(address='/dev/log')
|
||||
syslog.setFormatter(formatter)
|
||||
logger.addHandler(syslog)
|
||||
files_to_keep.append(syslog.socket)
|
||||
elif options['log_handler'] == 'file':
|
||||
if not logfile:
|
||||
logfile = '%s.log' % name
|
||||
if logdir:
|
||||
logfile = os.path.join(logdir, logfile)
|
||||
logfile = logging.FileHandler(logfile)
|
||||
logfile.setFormatter(formatter)
|
||||
logger.addHandler(logfile)
|
||||
files_to_keep.append(logfile.stream)
|
||||
stdin, stdout, stderr = None, None, None
|
||||
else:
|
||||
stdin, stdout, stderr = sys.stdin, sys.stdout, sys.stderr
|
||||
|
||||
with daemon.DaemonContext(
|
||||
detach_process=daemonize,
|
||||
working_directory=options['working_directory'],
|
||||
pidfile=pidlockfile.TimeoutPIDLockFile(pidfile,
|
||||
acquire_timeout=1,
|
||||
threaded=False),
|
||||
stdin=stdin,
|
||||
stdout=stdout,
|
||||
stderr=stderr,
|
||||
uid=options['uid'],
|
||||
gid=options['gid'],
|
||||
files_preserve=files_to_keep):
|
||||
main(args)
|
||||
@@ -35,9 +35,6 @@ import webob.dec
|
||||
import webob.exc
|
||||
|
||||
|
||||
logging.getLogger("routes.middleware").addHandler(logging.StreamHandler())
|
||||
|
||||
|
||||
def run_server(application, port):
|
||||
"""Run a WSGI server with the given application."""
|
||||
sock = eventlet.listen(('0.0.0.0', port))
|
||||
@@ -67,48 +64,7 @@ class Server(object):
|
||||
eventlet.wsgi.server(socket, application, custom_pool=self.pool)
|
||||
|
||||
|
||||
class Application(object):
|
||||
# TODO(gundlach): I think we should toss this class, now that it has no
|
||||
# purpose.
|
||||
"""Base WSGI application wrapper. Subclasses need to implement __call__."""
|
||||
|
||||
def __call__(self, environ, start_response):
|
||||
r"""Subclasses will probably want to implement __call__ like this:
|
||||
|
||||
@webob.dec.wsgify
|
||||
def __call__(self, req):
|
||||
# Any of the following objects work as responses:
|
||||
|
||||
# Option 1: simple string
|
||||
res = 'message\n'
|
||||
|
||||
# Option 2: a nicely formatted HTTP exception page
|
||||
res = exc.HTTPForbidden(detail='Nice try')
|
||||
|
||||
# Option 3: a webob Response object (in case you need to play with
|
||||
# headers, or you want to be treated like an iterable, or or or)
|
||||
res = Response();
|
||||
res.app_iter = open('somefile')
|
||||
|
||||
# Option 4: any wsgi app to be run next
|
||||
res = self.application
|
||||
|
||||
# Option 5: you can get a Response object for a wsgi app, too, to
|
||||
# play with headers etc
|
||||
res = req.get_response(self.application)
|
||||
|
||||
# You can then just return your response...
|
||||
return res
|
||||
# ... or set req.response and return None.
|
||||
req.response = res
|
||||
|
||||
See the end of http://pythonpaste.org/webob/modules/dec.html
|
||||
for more info.
|
||||
"""
|
||||
raise NotImplementedError("You must implement __call__")
|
||||
|
||||
|
||||
class Middleware(Application):
|
||||
class Middleware(object):
|
||||
"""
|
||||
Base WSGI middleware wrapper. These classes require an application to be
|
||||
initialized that will be called next. By default the middleware will
|
||||
@@ -116,18 +72,38 @@ class Middleware(Application):
|
||||
behavior.
|
||||
"""
|
||||
|
||||
def __init__(self, application): # pylint: disable-msg=W0231
|
||||
def __init__(self, application):
|
||||
self.application = application
|
||||
|
||||
def process_request(self, req):
|
||||
"""
|
||||
Called on each request.
|
||||
|
||||
If this returns None, the next application down the stack will be
|
||||
executed. If it returns a response then that response will be returned
|
||||
and execution will stop here.
|
||||
|
||||
"""
|
||||
return None
|
||||
|
||||
def process_response(self, response):
|
||||
"""Do whatever you'd like to the response."""
|
||||
return response
|
||||
|
||||
@webob.dec.wsgify
|
||||
def __call__(self, req): # pylint: disable-msg=W0221
|
||||
"""Override to implement middleware behavior."""
|
||||
return self.application
|
||||
def __call__(self, req):
|
||||
response = self.process_request(req)
|
||||
if response:
|
||||
return response
|
||||
response = req.get_response(self.application)
|
||||
return self.process_response(response)
|
||||
|
||||
|
||||
class Debug(Middleware):
|
||||
"""Helper class that can be inserted into any WSGI application chain
|
||||
to get information about the request and response."""
|
||||
"""
|
||||
Helper class that can be inserted into any WSGI application chain
|
||||
to get information about the request and response.
|
||||
"""
|
||||
|
||||
@webob.dec.wsgify
|
||||
def __call__(self, req):
|
||||
|
||||
@@ -175,3 +175,13 @@ def make_image_dict(image):
|
||||
|
||||
image_dict['properties'] = properties
|
||||
return image_dict
|
||||
|
||||
|
||||
def app_factory(global_conf, **local_conf):
|
||||
"""
|
||||
paste.deploy app factory for creating Glance reference implementation
|
||||
registry server apps
|
||||
"""
|
||||
conf = global_conf.copy()
|
||||
conf.update(local_conf)
|
||||
return API(conf)
|
||||
|
||||
@@ -439,3 +439,10 @@ class API(wsgi.Router):
|
||||
mapper.connect("/images/{id}", controller=controller, action="meta",
|
||||
conditions=dict(method=["HEAD"]))
|
||||
super(API, self).__init__(mapper)
|
||||
|
||||
|
||||
def app_factory(global_conf, **local_conf):
|
||||
"""paste.deploy app factory for creating Glance API server apps"""
|
||||
conf = global_conf.copy()
|
||||
conf.update(local_conf)
|
||||
return API(conf)
|
||||
|
||||
1
setup.py
1
setup.py
@@ -87,6 +87,7 @@ setup(
|
||||
],
|
||||
scripts=['bin/glance-api',
|
||||
'bin/glance-combined',
|
||||
'bin/glance-control',
|
||||
'bin/glance-manage',
|
||||
'bin/glance-registry',
|
||||
'bin/glance-upload'])
|
||||
|
||||
@@ -62,128 +62,4 @@ class TestConfig(unittest.TestCase):
|
||||
parser = optparse.OptionParser()
|
||||
config.add_common_options(parser)
|
||||
self.assertRaises(SystemExit, config.parse_options,
|
||||
parser,['--unknown'])
|
||||
|
||||
def test_options_to_conf(self):
|
||||
parser = optparse.OptionParser()
|
||||
config.add_common_options(parser)
|
||||
parsed_options, args = config.parse_options(parser)
|
||||
conf_options = config.options_to_conf(parsed_options)
|
||||
|
||||
expected_options = {'verbose': 'False', 'debug': 'False'}
|
||||
self.assertEquals(expected_options, conf_options)
|
||||
|
||||
def test_get_config_file_options(self):
|
||||
|
||||
# Test when no conf files are found...
|
||||
expected_options = {}
|
||||
conf_options = config.get_config_file_options(conf_dirs=['tests'])
|
||||
self.assertEquals(expected_options, conf_options)
|
||||
|
||||
# Test when a conf file is supplied and only DEFAULT
|
||||
# section is present
|
||||
with tempfile.NamedTemporaryFile() as f:
|
||||
contents = """[DEFAULT]
|
||||
verbose = True
|
||||
"""
|
||||
f.write(contents)
|
||||
f.flush()
|
||||
conf_file = f.name
|
||||
|
||||
expected_options = {'verbose': 'True'}
|
||||
conf_options = config.get_config_file_options(conf_file)
|
||||
self.assertEquals(expected_options, conf_options)
|
||||
|
||||
# Test when a conf file is supplied and it has a DEFAULT
|
||||
# section and another section called glance-api, with
|
||||
# no specified app_name when calling get_config_file_options()
|
||||
with tempfile.NamedTemporaryFile() as f:
|
||||
contents = """[DEFAULT]
|
||||
verbose = True
|
||||
|
||||
[glance-api]
|
||||
default_store = swift
|
||||
"""
|
||||
f.write(contents)
|
||||
f.flush()
|
||||
conf_file = f.name
|
||||
|
||||
expected_options = {'verbose': 'True',
|
||||
'default_store': 'swift'}
|
||||
conf_options = config.get_config_file_options(conf_file)
|
||||
self.assertEquals(expected_options, conf_options)
|
||||
|
||||
# Test when a conf file is supplied and it has a DEFAULT
|
||||
# section and another section called glance-api, with
|
||||
# specified app_name is NOT glance-api
|
||||
with tempfile.NamedTemporaryFile() as f:
|
||||
contents = """[DEFAULT]
|
||||
verbose = True
|
||||
|
||||
[glance-api]
|
||||
default_store = swift
|
||||
"""
|
||||
f.write(contents)
|
||||
f.flush()
|
||||
conf_file = f.name
|
||||
|
||||
expected_options = {'verbose': 'True'}
|
||||
app_name = 'glance-registry'
|
||||
conf_options = config.get_config_file_options(conf_file,
|
||||
app_name=app_name)
|
||||
self.assertEquals(expected_options, conf_options)
|
||||
|
||||
# Test when a conf file is supplied and it has a DEFAULT
|
||||
# section and two other sections. Check that the later section
|
||||
# overrides the value of the former section...
|
||||
with tempfile.NamedTemporaryFile() as f:
|
||||
contents = """[DEFAULT]
|
||||
verbose = True
|
||||
|
||||
[glance-api]
|
||||
default_store = swift
|
||||
|
||||
[glance-combined]
|
||||
default_store = s3
|
||||
"""
|
||||
f.write(contents)
|
||||
f.flush()
|
||||
conf_file = f.name
|
||||
|
||||
expected_options = {'verbose': 'True',
|
||||
'default_store': 's3'}
|
||||
conf_options = config.get_config_file_options(conf_file)
|
||||
self.assertEquals(expected_options, conf_options)
|
||||
|
||||
def test_parse_options_with_defaults(self):
|
||||
# Test the integration of parse_options() with a set
|
||||
# of defaults. These defaults generally come from a
|
||||
# configuration file
|
||||
defaults = {'verbose': 'on'}
|
||||
parser = optparse.OptionParser()
|
||||
config.add_common_options(parser)
|
||||
parsed_options, args = config.parse_options(parser, defaults=defaults)
|
||||
|
||||
expected_options = {'verbose': True, 'debug': False}
|
||||
self.assertEquals(expected_options, parsed_options)
|
||||
|
||||
# Write a sample conf file and merge the conf file defaults
|
||||
# with the parsed options.
|
||||
with tempfile.NamedTemporaryFile() as f:
|
||||
contents = """[DEFAULT]
|
||||
verbose = True
|
||||
debug = off
|
||||
"""
|
||||
f.write(contents)
|
||||
f.flush()
|
||||
conf_file = f.name
|
||||
|
||||
expected_options = {'verbose': True,
|
||||
'debug': False}
|
||||
conf_options = config.get_config_file_options(conf_file)
|
||||
parser = optparse.OptionParser()
|
||||
config.add_common_options(parser)
|
||||
parsed_options, args = config.parse_options(parser,
|
||||
defaults=conf_options)
|
||||
|
||||
self.assertEquals(expected_options, parsed_options)
|
||||
parser, ['--unknown'])
|
||||
|
||||
@@ -4,8 +4,7 @@ pep8==0.5.0
|
||||
pylint==0.19
|
||||
anyjson
|
||||
eventlet>=0.9.12
|
||||
lockfile==0.8
|
||||
python-daemon==1.5.5
|
||||
PasteDeploy
|
||||
routes
|
||||
webob
|
||||
wsgiref
|
||||
|
||||
Reference in New Issue
Block a user