Folsom continued.

1. Update how status is returned/structured/shown
2. Cleanup documentation.
This commit is contained in:
Joshua Harlow 2012-08-17 14:44:07 -07:00
parent 6c019b0f0b
commit 5fbbd572af
29 changed files with 251 additions and 619 deletions

View File

@ -41,13 +41,13 @@ class PhaseFunctors(object):
class Action(object):
__meta__ = abc.ABCMeta
def __init__(self, distro, cfg, root_dir, name, **kargs):
def __init__(self, distro, cfg, root_dir, name, **kwargs):
self.distro = distro
self.cfg = cfg
self.keep_old = kargs.get('keep_old', False)
self.force = kargs.get('force', False)
self.root_dir = root_dir
self.name = name
self.keep_old = kwargs.get('keep_old', False)
self.force = kwargs.get('force', False)
@property
def lookup_name(self):

View File

@ -17,6 +17,7 @@
from anvil import action
from anvil import colorizer
from anvil import log
from anvil import utils
from anvil.action import PhaseFunctors
@ -28,6 +29,10 @@ from anvil.components import (STATUS_INSTALLED, STATUS_STARTED,
class StatusAction(action.Action):
def __init__(self, distro, cfg, root_dir, name, **kwargs):
action.Action.__init__(self, distro, cfg, root_dir, name, **kwargs)
self.show_full = kwargs.get('show_full')
@property
def lookup_name(self):
return 'running'
@ -44,16 +49,30 @@ class StatusAction(action.Action):
return colorizer.quote(status, quote_color='red')
def _print_status(self, component, result):
if isinstance(result, (dict)):
LOG.info("Status of %s is:", colorizer.quote(component.name))
for (name, status) in result.items():
LOG.info("|-- %s --> %s.", colorizer.quote(name, quote_color='blue'), self._quote_status(status))
elif isinstance(result, (list, set)):
LOG.info("Status of %s is:", colorizer.quote(component.name))
for status in result:
LOG.info("|-- %s.", self._quote_status(status))
if not result:
LOG.info("Status of %s is %s.", colorizer.quote(component.name), self._quote_status(STATUS_UNKNOWN))
elif len(result) == 1:
s = result[0]
if s.name and s.name != component.name:
LOG.info("Status of %s (%s) is %s.", colorizer.quote(component.name), s.name, self._quote_status(s.status))
else:
LOG.info("Status of %s is %s.", colorizer.quote(component.name), self._quote_status(s.status))
if self.show_full and s.details:
det = utils.truncate_text(s.details, max_len=8192, from_bottom=True)
for line in det.splitlines():
line = line.replace("\t", "\\t")
line = line.replace("\r", "\\r")
LOG.info("%s>> %s", " " * 2, line)
else:
LOG.info("Status of %s is %s.", colorizer.quote(component.name), self._quote_status(result))
LOG.info("Status of %s is:", colorizer.quote(component.name))
for s in result:
LOG.info("|-- %s is %s.", s.name, self._quote_status(s.status))
if self.show_full and s.details:
det = utils.truncate_text(s.details, max_len=8192, from_bottom=True)
for line in det.splitlines():
line = line.replace("\t", "\\t")
line = line.replace("\r", "\\r")
LOG.info("%s>> %s", " " * 4, line)
def _run(self, persona, component_order, instances):
self._run_phase(

View File

@ -57,6 +57,11 @@ STATUS_INSTALLED = 'installed'
STATUS_STARTED = "started"
STATUS_STOPPED = "stopped"
STATUS_UNKNOWN = "unknown"
class ProgramStatus(object):
def __init__(self, status, name=None, details=''):
self.name = name
self.status = status
self.details = details
####
@ -461,100 +466,89 @@ class ProgramRuntime(component.Component):
mp['APP_NAME'] = app_name
return mp
def _fetch_run_type(self):
return self.cfg.getdefaulted("DEFAULT", "run_type", 'anvil.runners.fork:ForkRunner')
def start(self):
# Anything to start?
am_started = 0
apps_to_start = self.apps_to_start
if not apps_to_start:
return am_started
# Select how we are going to start it
run_type = self._fetch_run_type()
starter = importer.import_entry_point(run_type)(self)
for app_info in apps_to_start:
app_name = app_info["name"]
app_pth = app_info.get("path", app_name)
app_dir = app_info.get("app_dir", self.get_option('app_dir'))
# Adjust the program options now that we have real locations
program_opts = utils.param_replace_list(self.app_options(app_name), self.app_params(app_name))
# Start it with the given settings
LOG.debug("Starting %r using %r", app_name, run_type)
details_fn = starter.start(app_name, app_pth=app_pth, app_dir=app_dir, opts=program_opts)
LOG.info("Started %s details are in %s", colorizer.quote(app_name), colorizer.quote(details_fn))
# This trace is used to locate details about what to stop
self.tracewriter.app_started(app_name, details_fn, run_type)
if app_info.get('sleep_time'):
LOG.info("%s requested a %s second sleep time, please wait...", colorizer.quote(app_name), app_info.get('sleep_time'))
sh.sleep(app_info.get('sleep_time'))
am_started += 1
run_type = self.cfg.getdefaulted("DEFAULT", "run_type", 'anvil.runners.fork:ForkRunner')
starter_cls = importer.import_entry_point(run_type)
starter = starter_cls(self)
for i, app_info in enumerate(self.apps_to_start):
self._start_app(app_info, start)
am_started = i + 1
self._post_app_start(app_info)
return am_started
def _start_app(self, app_info, run_type, starter):
app_name = app_info["name"]
app_pth = app_info.get("path", app_name)
app_dir = app_info.get("app_dir", self.get_option('app_dir'))
program_opts = utils.param_replace_list(self.app_options(app_name), self.app_params(app_name))
LOG.debug("Starting %r using %r", app_name, starter)
details_fn = starter.start(app_name, app_pth=app_pth, app_dir=app_dir, opts=program_opts)
LOG.info("Started sub-program %s.", colorizer.quote(app_name))
# This trace is used to locate details about what/how to stop
self.tracewriter.app_started(app_name, details_fn, run_type)
def _post_app_start(self, app_info):
if 'sleep_time' in app_info:
LOG.info("%s requested a %s second sleep time, please wait...", colorizer.quote(app_name), app_info.get('sleep_time'))
sh.sleep(float(app_info.get('sleep_time')))
def _locate_investigators(self, apps_started):
investigators = dict()
to_investigate = list()
for (app_name, trace_fn, how) in apps_started:
investigator_created = {}
to_investigate = []
for (app_name, trace_fn, run_type) in apps_started:
inv_cls = None
try:
inv_cls = importer.import_entry_point(how)
inv_cls = importer.import_entry_point(run_type)
except RuntimeError as e:
LOG.warn("Could not load class %s which should be used to investigate %s: %s",
colorizer.quote(how), colorizer.quote(app_name), e)
continue
investigator = None
if inv_cls in investigators:
investigator = investigators[inv_cls]
if inv_cls in investigator_created:
investigator = investigator_created[inv_cls]
else:
investigator = inv_cls(self)
investigators[inv_cls] = investigator
investigator_created[inv_cls] = investigator
to_investigate.append((app_name, investigator))
return to_investigate
def stop(self):
# Anything to stop??
killed_am = 0
apps_started = self.tracereader.apps_started()
apps_started = 0
try:
apps_started = self.tracereader.apps_started()
except excp.NoTraceException:
pass
if not apps_started:
return killed_am
to_kill = self._locate_investigators(apps_started)
for (app_name, handler) in to_kill:
handler.stop(app_name)
handler.unconfigure()
killed_am += 1
if len(apps_started) == killed_am:
sh.unlink(self.tracereader.filename())
return killed_am
def _multi_status(self):
def status(self):
statii = []
apps_started = None
try:
apps_started = self.tracereader.apps_started()
except excp.NoTraceException:
return None
pass
if not apps_started:
return None
else:
to_check = self._locate_investigators(apps_started)
results = dict()
for (name, handler) in to_check:
try:
results[name] = handler.status(name)
except AttributeError:
pass # Not all handlers can implement this..
return results
def _status(self):
return STATUS_UNKNOWN
def status(self):
stat = self._multi_status()
if not stat:
stat = self._status()
if not stat:
stat = STATUS_UNKNOWN
return stat
return statii
to_check = self._locate_investigators(apps_started)
for (name, handler) in to_check:
(status, details) = handler.status(name)
statii.append(ProgramStatus(name=name,
status=status,
details=details))
return statii
def restart(self):
return 0

View File

@ -151,16 +151,16 @@ class DBRuntime(comp.EmptyRuntime):
self.wait_time = max(self.cfg.getint('DEFAULT', 'service_wait_seconds'), 1)
def _get_run_actions(self, act, exception_cls):
dbtype = self.cfg.get("db", "type")
distro_options = self.distro.get_command_config(dbtype)
db_type = self.cfg.get("db", "type")
distro_options = self.distro.get_command_config(db_type)
if distro_options is None:
raise NotImplementedError(BASE_ERROR % (act, dbtype))
return self.distro.get_command(dbtype, act)
raise NotImplementedError(BASE_ERROR % (act, db_type))
return self.distro.get_command(db_type, act)
def start(self):
if self._status() != comp.STATUS_STARTED:
startcmd = self._get_run_actions('start', excp.StartException)
sh.execute(*startcmd, run_as_root=True, check_exit_code=True)
if self.status()[0].status != comp.STATUS_STARTED:
start_cmd = self._get_run_actions('start', excp.StartException)
sh.execute(*start_cmd, run_as_root=True, check_exit_code=True)
LOG.info("Please wait %s seconds while it starts up." % self.wait_time)
sh.sleep(self.wait_time)
return 1
@ -168,29 +168,33 @@ class DBRuntime(comp.EmptyRuntime):
return 0
def stop(self):
if self._status() != comp.STATUS_STOPPED:
stopcmd = self._get_run_actions('stop', excp.StopException)
sh.execute(*stopcmd, run_as_root=True, check_exit_code=True)
if self.status()[0].status != comp.STATUS_STOPPED:
stop_cmd = self._get_run_actions('stop', excp.StopException)
sh.execute(*stop_cmd, run_as_root=True, check_exit_code=True)
return 1
else:
return 0
def restart(self):
LOG.info("Restarting your database.")
restartcmd = self._get_run_actions('restart', excp.RestartException)
sh.execute(*restartcmd, run_as_root=True, check_exit_code=True)
LOG.info("Please wait %s seconds while it restarts." % self.wait_time)
restart_cmd = self._get_run_actions('restart', excp.RestartException)
sh.execute(*restart_cmd, run_as_root=True, check_exit_code=True)
LOG.info("Please wait %s seconds while it restarts.", self.wait_time)
sh.sleep(self.wait_time)
return 1
def _status(self):
statuscmd = self._get_run_actions('status', excp.StatusException)
(sysout, stderr) = sh.execute(*statuscmd, run_as_root=True, check_exit_code=False)
combined = (str(sysout) + str(stderr)).lower()
def status(self):
status_cmd = self._get_run_actions('status', excp.StatusException)
(sysout, stderr) = sh.execute(*status_cmd, run_as_root=True, check_exit_code=False)
combined = (sysout + stderr).lower()
st = comp.STATUS_UNKNOWN
if combined.find("running") != -1:
return comp.STATUS_STARTED
st = comp.STATUS_STARTED
elif combined.find("stop") != -1 or \
combined.find('unrecognized') != -1:
return comp.STATUS_STOPPED
else:
return comp.STATUS_UNKNOWN
st = comp.STATUS_STOPPED
return [
comp.ProgramStatus(name=self.cfg.get("db", "type"),
status=st,
details=(sysout + stderr).strip()),
]

View File

@ -194,7 +194,7 @@ class HorizonRuntime(comp.EmptyRuntime):
comp.EmptyRuntime.__init__(self, *args, **kargs)
def start(self):
if self._status() != comp.STATUS_STARTED:
if self.status()[0].status != comp.STATUS_STARTED:
start_cmd = self.distro.get_command('apache', 'start')
sh.execute(*start_cmd, run_as_root=True, check_exit_code=True)
return 1
@ -207,22 +207,25 @@ class HorizonRuntime(comp.EmptyRuntime):
return 1
def stop(self):
if self._status() != comp.STATUS_STOPPED:
if self.status()[0].status != comp.STATUS_STOPPED:
stop_cmd = self.distro.get_command('apache', 'stop')
sh.execute(*stop_cmd, run_as_root=True, check_exit_code=True)
return 1
else:
return 0
def _status(self):
def status(self):
status_cmd = self.distro.get_command('apache', 'status')
(sysout, stderr) = sh.execute(*status_cmd, run_as_root=True, check_exit_code=False)
combined = (str(sysout) + str(stderr)).lower()
combined = (sysout + stderr).lower()
st = comp.STATUS_UNKNOWN
if combined.find("is running") != -1:
return comp.STATUS_STARTED
st = comp.STATUS_STARTED
elif combined.find("not running") != -1 or \
combined.find("stopped") != -1 or \
combined.find('unrecognized') != -1:
return comp.STATUS_STOPPED
else:
return comp.STATUS_UNKNOWN
st = comp.STATUS_STOPPED
return [
comp.ProgramStatus(status=st,
details=(sysout + stderr).strip()),
]

View File

@ -81,27 +81,30 @@ class RabbitRuntime(comp.EmptyRuntime):
self.wait_time = max(self.cfg.getint('DEFAULT', 'service_wait_seconds'), 1)
def start(self):
if self._status() != comp.STATUS_STARTED:
if self.status()[0].status != comp.STATUS_STARTED:
self._run_cmd(self.distro.get_command('rabbit-mq', 'start'))
return 1
else:
return 0
def _status(self):
def status(self):
# This has got to be the worst status output.
#
# I have ever seen (its like a weird mix json+crap)
status_cmd = self.distro.get_command('rabbit-mq', 'status')
(sysout, stderr) = sh.execute(*status_cmd, check_exit_code=False, run_as_root=True)
combined = (str(sysout) + str(stderr)).lower()
st = comp.STATUS_UNKNOWN
combined = (sysout + stderr).lower()
if combined.find('nodedown') != -1 or \
combined.find("unable to connect to node") != -1 or \
combined.find('unrecognized') != -1:
return comp.STATUS_STOPPED
st = comp.STATUS_STOPPED
elif combined.find('running_applications') != -1:
return comp.STATUS_STARTED
else:
return comp.STATUS_UNKNOWN
st = comp.STATUS_STARTED
return [
comp.ProgramStatus(status=st,
details=(sysout + stderr).strip()),
]
def _run_cmd(self, cmd, check_exit=True):
# This seems to fix one of the bugs with rabbit mq starting and stopping
@ -124,8 +127,8 @@ class RabbitRuntime(comp.EmptyRuntime):
return 1
def stop(self):
if self._status() != comp.STATUS_STOPPED:
self._run_cmd(self.distro.get_command('rabbit-mq', 'stop'))
if self.status()[0].status != comp.STATUS_STOPPED:
self._run_cmd(self.distro.get_command('rabbitmq-server', 'stop'))
return 1
else:
return 0

View File

@ -107,7 +107,17 @@ def parse():
default=False)
parser.add_option_group(un_group)
# Extract only what we care about
status_group = OptionGroup(parser, "Status specific options")
status_group.add_option('-s', "--show",
action="store_true",
dest="show_full",
help="show the stderr/stdout log files if applicable when showing status (default: %default)",
default=False)
parser.add_option_group(status_group)
# Extract only what we care about, these will be passed
# to the constructor of actions as arguments
# so don't adjust the naming wily nilly...
(options, args) = parser.parse_args()
output = {}
output['dir'] = (options.dir or "")
@ -121,5 +131,6 @@ def parse():
output['verbosity'] = len(options.verbosity)
output['cli_overrides'] = (options.cli_overrides or [])
output['prompt_for_passwords'] = options.prompt_for_passwords
output['show_full'] = options.show_full
return output

View File

@ -57,7 +57,11 @@ class Packager(object):
if version:
# This won't work for all package versions (ie crazy names)
# but good enough for now...
p_version = pkg_resources.Requirement.parse(version)
if contains_version_check(version):
full_name = "%s%s" %(name, version)
else:
full_name = "%s==%s" %(name, version)
p_version = pkg_resources.Requirement.parse(full_name)
else:
p_version = NullVersion(name)
return p_version
@ -139,3 +143,10 @@ def get_packager(pkg_info, distro, default_packager_class):
p_instance = p_cls(distro, PackageRegistry())
FETCHED_PACKAGERS[p_cls] = p_instance
return p_instance
def contains_version_check(version):
for c in ['==', '>', "<", '<=', '>=']:
if version.find(c) != -1:
return True
return False

View File

@ -30,14 +30,10 @@ class Packager(pack.Packager):
def _make_pip_name(self, name, version):
if version is None:
return str(name)
found_char = False
for c in ['==', '>', "<", '<=', '>=']:
if version.find(c) != -1:
found_char = True
break
if found_char:
if pack.contains_version_check(version):
return "%s%s" % (name, version)
return "%s==%s" % (name, version)
else:
return "%s==%s" % (name, version)
def _get_pip_command(self):
return self.distro.get_command_config('pip')

View File

@ -36,5 +36,5 @@ class Runner(object):
pass
def status(self, app_name):
# Attempt to give the status of a app
return STATUS_UNKNOWN
# Attempt to give the status of a app + details
return (STATUS_UNKNOWN, '')

View File

@ -83,13 +83,15 @@ class ForkRunner(base.Runner):
def status(self, app_name):
trace_dir = self.runtime.get_option('trace_dir')
if not sh.isdir(trace_dir):
return STATUS_UNKNOWN
return (STATUS_UNKNOWN, '')
(pid_file, stderr_fn, stdout_fn) = self._form_file_names(FORK_TEMPL % (app_name))
pid = self._extract_pid(pid_file)
stderr = sh.load_file(stderr_fn)
stdout = sh.load_file(stderr_fn)
if pid and sh.is_running(pid):
return STATUS_STARTED
return (STATUS_STARTED, (stdout + stderr).strip())
else:
return STATUS_UNKNOWN
return (STATUS_UNKNOWN, (stdout + stderr).strip())
def _form_file_names(self, file_name):
trace_dir = self.runtime.get_option('trace_dir')

View File

@ -197,8 +197,8 @@ def execute(*cmd, **kwargs):
else:
# Log it anyway
if rc not in check_exit_code:
LOG.warn("A failure may of just happened when running command %r [%s] (%s, %s)",
str_cmd, rc, stdout, stderr)
LOG.debug("A failure may of just happened when running command %r [%s] (%s, %s)",
str_cmd, rc, stdout, stderr)
# See if a requested storage place was given for stderr/stdout
trace_writer = kwargs.get('trace_writer')
stdout_fn = kwargs.get('stdout_fn')

View File

@ -212,6 +212,18 @@ def to_bytes(text):
return byte_val
def truncate_text(text, max_len, from_bottom=False):
if len(text) < max_len:
return text
if not from_bottom:
return (text[0:max_len] + "...")
else:
text = text[::-1]
text = truncate_text(text, max_len)
text = text[::-1]
return text
def log_object(to_log, logger=None, level=logging.INFO, item_max_len=64):
if not to_log:
return

View File

@ -37,7 +37,7 @@ source_suffix = '.rst'
master_doc = 'index'
# General information about the project.
project = anvil_settings.PROG_NAME.upper()
project = 'ANVIL'
# The version info for the project you're documenting, acts as replacement for
# |version| and |release|, also used in various other places throughout the

View File

@ -6,13 +6,6 @@ ANVIL Documentation
.. rubric:: Everything about ANVIL, a set of **python** scripts and utilities to quickly deploy an OpenStack cloud.
Why the rename?
----
- Helps avoid confusion with devstack.org
- Allows our team to focus on any different features
- Creates a clearer goal as to where we diverge
- More awesomeness?
----
@ -26,5 +19,4 @@ Why the rename?
topics/qanda
topics/knownissues
topics/bugshugscode
topics/advanced/index

View File

@ -1,55 +0,0 @@
==========
Adding your own distribution
==========
Your mission...
=============
So you have decided you want to venture into the bowels of ANVIL
and want to get support for your latest and greatest distribution. This
wiki will hopefully make that adventure simpler by listing out the key
places, files and configs that may have to be adjusted to get that to
work. This tape will self-destruct in 5 seconds. 1..2..3..4..5, boom!
Steps
=====
Snapshot
--------
One of the most useful things to do will be to get a virtual machine
with your distribution and setup a stable state. Create a snapshot of
that stable state just in-case you *bork* your machine later on.
Logging
-------
Now turn ensure you run ``DEBUG/VERBOSE`` logging using ``-vv``. This
will be useful to see exactly what actions and commands are being ran
instead of the default ``INFO`` level logging which is just meant for
simple informational messages about the underlying actions which are
occurring.
Configs
-------
By looking at the config folder ``distros`` you should exactly which
packages and pips and commands are needed for each component by looking
at a similar distribution. So you first task is to determine exactly
what versions are available for your distribution. If a version doesnt
exist the you may need to resort to either using the `pypi`_ index or
having to package it yourself. If a version is to new, this is usually
ok (your mileage may vary) and if its to old then that might also not be
ok (your mileage may vary).
Try it
------
Now that you have provided a new `YAML`_ distro file you should be able
to run through the simple setup wiki and see if the install will pass.
If that does try starting and then seeing if everything has started up
correctly.
.. _pypi: http://pypi.python.org
.. _YAML: http://yaml.org/

View File

@ -1,60 +0,0 @@
==========
Adding your own persona
==========
Your mission...
=============
So you have decided you want to venture into the bowels of ANVIL
and want to alter what is installed/started/stopped, the order of what
is installed/started/stopped, what subsystems are activated (or the
component options). This wiki will hopefully make that adventure simpler
by listing out the key places, files and configs that may have to be
adjusted to get that to work. This tape will self-destruct in 5 seconds.
1..2..3..4..5, boom!
Steps
=====
Snapshot
--------
One of the most useful things to do will be to get a virtual machine
with your distribution and setup a stable state. Create a snapshot of
that stable state just in-case you *bork* your machine later on.
Logging
-------
Now turn ensure you run ``DEBUG/VERBOSE`` logging using ``-vv``. This
will be useful to see exactly what actions and commands are being ran
instead of the default ``INFO`` level logging which is just meant for
simple informational messages about the underlying actions which are
occurring.
Configs
-------
By looking at the config folder ``personas`` you should a file called
``devstack.sh.yaml``. This file contains the component order of
installation (ie the ``db`` before ``keystone``), a nice useful
description of the persona and subsystems for the previously specified
components and any options these components may have. So you first task
is to determine exactly what of these you wish to change (if any). Note
that changing the component order may not always work (ie typically
starting components are dependent, ie the message queue needs to be
started before nova). To add in new components check the ``distros``
folder to determine exactly what that component is named (typically this
is common) and alter the persona file as desired. To alter the
``subsystems`` or ``options`` section you will have to jump in the code
and check for what these values could be (TODO make that better).
Try it
------
Now that you have provided a new `YAML`_ persona file you should be able
to run the ``stack`` program with that persona through the ``-p``
option.
.. _YAML: http://yaml.org/

View File

@ -1,163 +0,0 @@
========
Design
========
How it works
------------
ANVIL is based along the following system design
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- Having shared components/actions be shared (using object oriented
practices)
- Having specific actions be isolated to its component (and easily
readable)
- Being simple enough to read yet following standard python software
development practices and patterns
Directory structure is the following
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- Parent classes located in *anvil/component.py*, it contains the
root install/uninstall/start/stop classes
- Subclasses in *anvil/components/*, it contains the individual
install classes for each openstack component
- Running modes implementations in *anvil/runners/* (ie fork,
screen, upstart)
- Packaging implementations in *anvil/packaging/*
- Image uploading/registry/management (for glance) in *anvil/image/*
- Shared classes and utils in *anvil/*
- Main entry point/s in *anvil/progs/*
- Other various tools in *tools/*
- Configuration in *conf/* (see below)
Example
~~~~~~~
Install object model
^^^^^^^^^^^^^^^^^^^^
Here is the **install** components (default set only) class hierarchy:
.. figure:: http://farm8.staticflickr.com/7043/6894250521_d882b770d1_o.png
:align: center
Classes
'''''''
From this example the root classes job are:
- Python install component
- Ensures *pips* are installed (child classes specify which pips)
- Ensures python directories (ie *setup.py*) are setup (child classes
specify which directories)
- Package install component
- Installs packages that are required for install (child classes
specify which packages)
- Sets up and performs parameter replacement on config files (child
classes specify which config files)
- Sets up symlinks to config or other files (child classes specify
these symlinks)
- Tracks what was setup so that it can be removed/uninstalled
- Component base (used by **all** install/uninstall/start/stop
component classes)
- Holds configuration object, component name, packaging and other
shared members…
- Allows for overriding of dependency function and pre-run verification
Functions
'''''''''
For a install class the following functions are activated (in the
following order by *anvil/actions.py*):
::
download()
Performs the main git download (or other download type) to the
application target directory.
::
configure()
Configures the components files (symlinks, configuration and logging
files…)
::
pre_install()
Child class specific function that can be used to do anything before
an install (ie set a ubuntu mysql pre-install root password)
::
install()
Installs distribution packages, python packages (*pip*), sets up
python directories (ie *python setup.py develop*) and any other
child class specific actions.
::
post_install()
Child class specific function that can be used to do anything after
an install (ie run *nova-manage db sync*)
Other object models
^^^^^^^^^^^^^^^^^^^
- `Start object model (for default set only)`_
- `Stop object model (for default set only)`_
- `Uninstall object model (for default set only)`_
Configuring
-----------
For those of you that are brave enough to change *stack* here are some
starting points.
conf/stack.ini
~~~~~~~~~~~~~~
Check out *conf/stack.ini* for various configuration settings applied
(branches, git repositories…). Check out the header of that file for how
the customized configuration values are parsed and what they may result
in.
conf/distros
~~~~~~~~~~~~
Check out *conf/distros* for the `YAML`_ files that describe
pkgs/cmds/pips for various distributions which are required by the
different OpenStack components to run correctly. The versions and pip
names listed for each distribution should be the correct version that is
known to work with a given OpenStack release.
conf/templates/
~~~~~~~~~~~~~~~
Check out *conf/templates/* for various component specific settings and
files. All of these files are *templates* with sections or text that
needs to be filled in by the ``stack`` script to become a *complete*
file.
These files may have strings of the format ``%NAME%`` where ``NAME``
will most often be adjusted to a real value by the *stack* script.
An example where this is useful is say for the following line:
::
admin_token = %SERVICE_TOKEN%
Since the script will either prompt for this value (or generate it for
you) we can not have this statically set in a configuration file.
.. _Start object model (for default set only): http://farm8.staticflickr.com/7046/6894981327_a583bcb4fc_o.png
.. _Stop object model (for default set only): http://farm8.staticflickr.com/7059/6894981341_e6d4901b20_o.png
.. _Uninstall object model (for default set only): http://farm8.staticflickr.com/7177/6894981357_fef65b28d3_o.png
.. _YAML: http://yaml.org/

View File

@ -1,11 +0,0 @@
===============
Advanced
===============
.. toctree::
:maxdepth: 2
addingyourowndistro
addingyourownpersona
design

View File

@ -25,13 +25,10 @@ Stable *tags* can also be downloaded:
https://github.com/yahoo/Openstack-Anvil/tags
**Note:** that for these tags you may have to edit ``conf/anvil.ini``
to point to tags other than ``master``
Bugs/Features
=============
Please use `githubs issue tracking system`_ or `launchpads issue tracking system`_ to report or follow bugs or to discuss features and get support.
Please use `launchpads issue tracking system`_ to report or follow bugs or to discuss features and get support.
Hacking
=============
@ -42,9 +39,8 @@ Feel free to hack but please try to follow the `hacking guidelines`_
Discussions
===========
Please either use launchpads email system or find us on ``irc.freenode.net`` in channel ``#openstack-anvil`` or in the main openstack dev channel ``#openstack-dev``. Feel free to bug us!
Please use launchpads blueprint/bug system for contacting us about bugs or new features (or submit them!). Much appreciated!
.. _apache version 2.0 license: https://github.com/yahoo/Openstack-Anvil/blob/master/LICENSE
.. _githubs issue tracking system: https://github.com/yahoo/Openstack-Anvil/issues
.. _launchpads issue tracking system: http://launchpad.net/anvil
.. _hacking guidelines: https://github.com/yahoo/Openstack-Anvil/blob/master/HACKING.md

View File

@ -2,33 +2,38 @@
Features
========
- Supports more than one distribution.
- Currently RHEL 6.2 (with `epel`_), Ubuntu 11.10, Fedora 16, Ubuntu 12.10 (seems to work)
- Supports dry-run mode (to see what *would* happen)
- Supports varying installation *personas*
- A single ``anvil.ini`` file that shows common configuration
- Supports install/uninstall/starting/stopping of OpenStack components.
- In various styles (daemonizing via `forking`_, `screen`_, `upstart`_)
- Multi distribution installs via a single tool (TODO: fix for folsom)
- A single ``anvil.ini`` file that shows common/component configuration
- Supports the following *actions* on the various of OpenStack components.
#. **Installing**: downloading, installing dependencies (`pypi`_ and distribution packaging specifics)
and configuring component files and symlinks
#. **Starting**: starting of the components sub-programs with
the needed configuration via the common `daemon`_ model (with a ``pid``, ``stderr`` and ``stdout`` file set)
#. **Stopping**: stopping of the previously started components
#. **Uninstalling**: removing installed configuration, undoing of installed files/directories,
and removing of packaging to get back to an initial 'clean' state
#. **Testing**: running each components unit tests (and in the future performing a simple set of integration tests)
#. **Packaging**: creating a basic set of packages for the desired distributions
#. **Status**: checking the status of the running components sub-programs
- Supports dry-run mode (to see what *would* happen for each action)
- Tracking of all actions taken by a component via tracking like files.
- Written in python so it matches the style of other `OpenStack`_ components.
- Code decoupling (thus encouraging re-use by others)
#. Components/actions are isolated as individual classes (and so on). This
decouples component installation from the action and decoupling of the
commands/packages/pips the component will use to install itself from the
component...
#. Supports installation *personas* that define what is to be installed, thus
decoupling the 'what' from the 'how'
- Extensively documented distribution specifics
- Packages and pip (with versions known to work!) dependencies
- Any needed distribution specific actions (ie service names…)
- Follows standard software development practices (for everyones sanity).
- Functions, classes, objects and more (oh my!)
- Still *readable* by someone with limited python knowledge.
- The ability to be unit-tested!
- Extensive logging
- Progress resuming so that when you install you can ``ctrl+c`` ``./smithy`` and resume later (where applicable).
- Extensive logging (and debug mode)
.. _epel: http://fedoraproject.org/wiki/EPEL
.. _forking: http://users.telenet.be/bartl/classicperl/fork/all.html
.. _screen: http://www.manpagez.com/man/1/screen/
.. _upstart: http://upstart.ubuntu.com/
.. _OpenStack: http://openstack.org/
.. _pypi: http://pypi.python.org/pypi
.. _daemon: http://en.wikipedia.org/wiki/Daemon_(computing)

View File

@ -16,18 +16,9 @@ Prerequisites
Linux
-----
One of the tested Linux distributions (RHEL 6.2, Ubuntu 11.10, Fedora
16)
One of the tested Linux distributions (RHEL 6.2+ until further updated)
You can get Ubuntu 11.10 (**64-bit** is preferred) from
http://releases.ubuntu.com/11.10/
You can get RHEL 6.2 (**64-bit** is preferred) from
http://rhn.redhat.com/.
You can get Fedora 16 (**64-bit** is preferred) from
https://fedoraproject.org/get-fedora, so dont worry if you do not have
a RHN subscription.
You can get RHEL 6.2+ (**64-bit** is preferred) from http://rhn.redhat.com/.
Networking
----------
@ -84,14 +75,12 @@ Installation
Pre-setup
---------
Since RHEL/Fedora requires a `tty`_ to perform ``sudo`` commands we need
Since RHEL requires a `tty`_ to perform ``sudo`` commands we need
to disable this so ``sudo`` can run without a `tty`_. This seems needed
since nova and other components attempt to do ``sudo`` commands. This
isnt possible in RHEL/Fedora unless you disable this (since those
isnt possible in RHEL unless you disable this (since those
instances wont have a `tty`_ ).
**For RHEL and Fedora 16:**
::
$ sudo visudo
@ -127,38 +116,10 @@ This can be typically solved by running the following (and then updating ``anvil
$ sudo chmod -R a+rwx /home/openstack
**For Ubuntu:**
You are off the hook.
Users
-----
We need to add a admin user so that horizon can run under `apache`_.
**For Ubuntu:**
::
$ apt-get install sudo -y
$ sudo adduser horizon
$ sudo adduser horizon admin
**For RHEL/Fedora 16:**
You are off the hook as long as your user has ``sudo`` access.
Get git!
--------
**For Ubuntu:**
::
$ sudo apt-get install git -y
**For RHEL/Fedora 16:**
::
$ sudo yum install git -y
@ -173,52 +134,9 @@ Well grab the latest version of ANVIL via git:
$ git clone git://github.com/yahoo/Openstack-Anvil.git anvil
Now setup the prerequisites needed to run (select the appropriate shell script for your distro):
::
$ cd anvil/warmups && sudo ./$DISTRO.sh
Configuration
-------------
Apache configuration
~~~~~~~~~~~~~~~~~~~~
We need to adjust the configuration of ANVIL to reflect the above
user (``iff you created a user``).
Open ``conf/anvil.ini``
**Change section:**
::
[horizon]
# What user will apache be serving from.
#
# Root will typically not work (for apache on most distros)
# sudo adduser <username> then sudo adduser <username> admin will be what you want to set this up (in ubuntu)
# I typically use user "horizon" for ubuntu and the runtime user (who will have sudo access) for RHEL.
#
# NOTE: If blank the currently executing user will be used.
apache_user = ${APACHE_USER:-}
**To:**
::
[horizon]
# What user will apache be serving from.
#
# Root will typically not work (for apache on most distros)
# sudo adduser <username> then sudo adduser <username> admin will be what you want to set this up (in ubuntu)
# I typically use user "horizon" for ubuntu and the runtime user (who will have sudo access) for RHEL.
#
# NOTE: If blank the currently executing user will be used.
apache_user = ${APACHE_USER:-horizon}
Network configuration
~~~~~~~~~~~~~~~~~~~~~
@ -269,7 +187,7 @@ Now install *OpenStacks* components by running the following:
::
sudo ./smithy -a install -d ~/openstack
sudo ./smithy -a install
You should see a set of distribution packages and/or pips being
installed, python setups occurring and configuration files being written
@ -285,7 +203,7 @@ Now that you have installed *OpenStack* you can now start your
::
sudo ./smithy -a start -d ~/openstack
sudo ./smithy -a start
If you desire more informational output add a ``-v`` or a ``-vv`` to
that command.
@ -332,29 +250,28 @@ EC2 apis run the following to get your EC2 certs:
::
euca.sh $OS_USERNAME $OS_TENANT_NAME
./euca.sh $OS_USERNAME $OS_TENANT_NAME
It broke?
~~~~~~~~~
*Otherwise* you may have to look at the output of what was started. To
accomplish this you may have to log at the ``stderr`` and ``stdout``
that is being generated from the running *OpenStack* process (by default
they are forked as daemons). For this information check the output of
the start command for a line like
``Check * for traces of what happened``. This is usually a good starting
point, to check out those files contents and then look up the files that
contain the applications `PID`_ and ``stderr`` and ``stdout``.
First run the following to check the status of each component.
If the install section had warning messages or exceptions were thrown
there, that may also be the problem. Sometimes running the uninstall
section below will clean this up, your mileage may vary though.
::
Another tip is to edit run with more verbose logging by running with the
following ``-v`` option or the ``-vv`` option. This may give you more
insights by showing you what was executed/installed/configured
(uninstall & start by installing again to get the additional logging
output).
sudo ./smithy -a status
If you do not see all green status then you should run the following and see
if any of the ``stderr`` and ``stdout`` files will give you more information
about what is occuring
::
sudo ./smithy -a status --show
This will dump out those files (truncated to not be to verbose) so that anything
peculaliar can be seen. If nothing can be then go to the installation directory (typically ``~/openstack``)
and check the ``traces`` directory of each component and check if anything looks fishy.
Stopping
--------
@ -364,7 +281,7 @@ the following:
::
sudo ./smithy -a stop -d ~/openstack
sudo ./smithy -a stop
You should see a set of stop actions happening and ``stderr`` and
``stdout`` and ``pid`` files being removed (if you desire more
@ -391,7 +308,7 @@ can uninstall them by running the following:
::
sudo ./smithy -a uninstall -d ~/openstack
sudo ./smithy -a uninstall
You should see a set of packages, configuration and directories, being
removed (if you desire more informational output add a ``-v`` or a

View File

@ -1,24 +1,19 @@
===============
Goals
Goals
===============
- To aid developers getting involved with `OpenStack`_!
- To quickly build developer `OpenStack`_ environments in a clean
environment (as well as start, stop, and uninstall those
environments) with as little baggage as possible.
- To describe working configurations of `OpenStack`_.
- Which code branches work together?
- What do config files look like for those branches?
- What packages are needed for installation for a given
distribution?
- How is a component installed/configured/ran?
- To make it easier for developers to dive into `OpenStack`_ so that
they can productively contribute without having to understand every
part of the system at once.
- To make it easy to prototype cross-project features.
- To have an installer that works!
.. _OpenStack: http://openstack.org/

View File

@ -14,10 +14,6 @@ There is a script in ``tools/clear-net-ubuntu.sh`` that might help with this.
RHEL 6.2
--------
- *numpy* (for novnc) pulls in *python-nose* which we are installing
from *EPEL* and *symlinking* so that we dont have to modify github
code directly but the *numpy* dependency cant be installed with the
previous symlink. We are currently just using *pip* to fix this.
- Fixing up the network on uninstall doesnt seem to be 100% correct
We might need a script like the ``tools/clear-net-ubuntu.sh`` to help in this situation.

View File

@ -4,11 +4,6 @@
Questions and Answers
===============
Can I use ANVIL for production?
------------------------------------
Up to u! Beware of the sea and the black waters!
How do I get program usage?
---------------------------
@ -19,29 +14,11 @@ How do I get program usage?
How do I run a specific OpenStack milestone?
--------------------------------------------
OpenStack milestones have tags set in the git repo. Set the appropriate
setting in the **branch** variables in *conf/anvil.ini*.
OpenStack milestones have tags set in the git repo. Anvil also has the same
tags so please checkout the corresponding tag for anvil to match the OpenStack
milestone you wish to use.
**Note:** Swift is on its own release schedule so pick a tag in the
Swift repo that is just before the milestone release.
For example:
::
# Quantum client git repo
quantum_client_repo = git://github.com/openstack/python-quantumclient.git
quantum_client_branch = essex-3
# Melange service
melange_repo = git://github.com/openstack/melange.git
melange_branch = essex-3
# Python melange client library
melangeclient_repo = git://github.com/openstack/python-melangeclient.git
melangeclient_branch = essex-3
OMG the images take forever to download!
`OMG` the images take forever to download!
----------------------------------------
Sometimes the images that will be uploaded to glance take a long time to

View File

@ -37,22 +37,6 @@ To resolve this the following seems to work:
sudo apt-get remove --purge $MYSQL_PKGS
Libguestfs not syncing
---------------------
On RHEL 6.2 people have seen files not being synced when the temporary file location is unmounted.
Getting a newer version of libguestfs seems to help. To do this follow the ``README`` at
http://people.redhat.com/~rjones/libguestfs-RHEL-6.3-preview/. Hopefully this will not be a problem
on RHEL 6.3.
Then run:
::
sudo yum -y install libguestfs* guestfs*
Horizon dead on start
---------------------

View File

@ -21,8 +21,8 @@ will do (with installation in ``~/openstack``) try:
$ sudo ./smithy -d ~/openstack -a install --dryrun
With more information/debugging/auditing output try:
With more information via debug statements output try:
::
$ sudo ./smithy -d ~/openstack -a install -vv
$ sudo ./smithy -d ~/openstack -a install -v

View File

@ -2,11 +2,13 @@
Important!
==========
**Warning:** Be sure to carefully read ``smithy`` and any other scripts
Warning!
==========
Be sure to carefully read ``smithy`` and any other scripts
you execute before you run them, as they install software and may alter
your networking configuration. We strongly recommend that you run
``smithy`` in a clean and disposable virtual machine when you are first
getting started.
your networking configuration.
.. _epel: http://fedoraproject.org/wiki/EPEL
.. _forking: http://users.telenet.be/bartl/classicperl/fork/all.html

8
smithy
View File

@ -230,7 +230,10 @@ def run(args):
if config.opts_cache:
LOG.info("After action %s your settings which were applied are:", colorizer.quote(action))
table = OrderedDict()
for section in sorted(list((config.opts_read + config.opts_set).keys())):
all_read_set = {}
all_read_set.update(config.opts_read)
all_read_set.update(config.opts_set)
for section in sorted(list(all_read_set.keys())):
options = set()
if section in config.opts_read:
options.update(list(config.opts_read[section]))
@ -238,8 +241,7 @@ def run(args):
options.update(list(config.opts_set[section]))
option_values = {}
for option in options:
cache_key = cfg.make_id(section, option)
option_values[cache_key] = config.opts_cache[cache_key]
option_values[option] = config.opts_cache[cfg.make_id(section, option)]
table[section] = option_values
utils.log_object(table, item_max_len=80)