Resync le charm helpers

This commit is contained in:
Liam Young 2015-06-04 09:44:46 +01:00
commit 3706bff3a0
14 changed files with 320 additions and 141 deletions

View File

@ -1,4 +1,4 @@
branch: lp:~le-charmers/charm-helpers/leadership-election branch: lp:charm-helpers
destination: hooks/charmhelpers destination: hooks/charmhelpers
include: include:
- core - core

View File

@ -1,4 +1,4 @@
branch: lp:~le-charmers/charm-helpers/leadership-election branch: lp:charm-helpers
destination: tests/charmhelpers destination: tests/charmhelpers
include: include:
- contrib.amulet - contrib.amulet

View File

@ -53,6 +53,8 @@ from charmhelpers.core.strutils import (
bool_from_string, bool_from_string,
) )
DC_RESOURCE_NAME = 'DC'
class HAIncompleteConfig(Exception): class HAIncompleteConfig(Exception):
pass pass
@ -105,6 +107,27 @@ def is_clustered():
return False return False
def is_crm_dc():
"""
Determine leadership by querying the pacemaker Designated Controller
"""
cmd = ['crm', 'status']
try:
status = subprocess.check_output(cmd, stderr=subprocess.STDOUT)
if not isinstance(status, six.text_type):
status = six.text_type(status, "utf-8")
except subprocess.CalledProcessError:
return False
current_dc = ''
for line in status.split('\n'):
if line.startswith('Current DC'):
# Current DC: juju-lytrusty-machine-2 (168108163) - partition with quorum
current_dc = line.split(':')[1].split()[0]
if current_dc == get_unit_hostname():
return True
return False
@retry_on_exception(5, base_delay=2, exc_type=CRMResourceNotFound) @retry_on_exception(5, base_delay=2, exc_type=CRMResourceNotFound)
def is_crm_leader(resource, retry=False): def is_crm_leader(resource, retry=False):
""" """
@ -114,6 +137,8 @@ def is_crm_leader(resource, retry=False):
We allow this operation to be retried to avoid the possibility of getting a We allow this operation to be retried to avoid the possibility of getting a
false negative. See LP #1396246 for more info. false negative. See LP #1396246 for more info.
""" """
if resource == DC_RESOURCE_NAME:
return is_crm_dc()
cmd = ['crm', 'resource', 'show', resource] cmd = ['crm', 'resource', 'show', resource]
try: try:
status = subprocess.check_output(cmd, stderr=subprocess.STDOUT) status = subprocess.check_output(cmd, stderr=subprocess.STDOUT)

View File

@ -256,11 +256,14 @@ def network_manager():
def parse_mappings(mappings): def parse_mappings(mappings):
parsed = {} parsed = {}
if mappings: if mappings:
mappings = mappings.split(' ') mappings = mappings.split()
for m in mappings: for m in mappings:
p = m.partition(':') p = m.partition(':')
if p[1] == ':': key = p[0].strip()
parsed[p[0].strip()] = p[2].strip() if p[1]:
parsed[key] = p[2].strip()
else:
parsed[key] = ''
return parsed return parsed
@ -283,13 +286,13 @@ def parse_data_port_mappings(mappings, default_bridge='br-data'):
Returns dict of the form {bridge:port}. Returns dict of the form {bridge:port}.
""" """
_mappings = parse_mappings(mappings) _mappings = parse_mappings(mappings)
if not _mappings: if not _mappings or list(_mappings.values()) == ['']:
if not mappings: if not mappings:
return {} return {}
# For backwards-compatibility we need to support port-only provided in # For backwards-compatibility we need to support port-only provided in
# config. # config.
_mappings = {default_bridge: mappings.split(' ')[0]} _mappings = {default_bridge: mappings.split()[0]}
bridges = _mappings.keys() bridges = _mappings.keys()
ports = _mappings.values() ports = _mappings.values()
@ -309,6 +312,8 @@ def parse_vlan_range_mappings(mappings):
Mappings must be a space-delimited list of provider:start:end mappings. Mappings must be a space-delimited list of provider:start:end mappings.
The start:end range is optional and may be omitted.
Returns dict of the form {provider: (start, end)}. Returns dict of the form {provider: (start, end)}.
""" """
_mappings = parse_mappings(mappings) _mappings = parse_mappings(mappings)

View File

@ -53,9 +53,13 @@ from charmhelpers.contrib.network.ip import (
get_ipv6_addr get_ipv6_addr
) )
from charmhelpers.contrib.python.packages import (
pip_create_virtualenv,
pip_install,
)
from charmhelpers.core.host import lsb_release, mounts, umount from charmhelpers.core.host import lsb_release, mounts, umount
from charmhelpers.fetch import apt_install, apt_cache, install_remote from charmhelpers.fetch import apt_install, apt_cache, install_remote
from charmhelpers.contrib.python.packages import pip_install
from charmhelpers.contrib.storage.linux.utils import is_block_device, zap_disk from charmhelpers.contrib.storage.linux.utils import is_block_device, zap_disk
from charmhelpers.contrib.storage.linux.loopback import ensure_loopback_device from charmhelpers.contrib.storage.linux.loopback import ensure_loopback_device
@ -497,7 +501,17 @@ def git_install_requested():
requirements_dir = None requirements_dir = None
def git_clone_and_install(projects_yaml, core_project): def _git_yaml_load(projects_yaml):
"""
Load the specified yaml into a dictionary.
"""
if not projects_yaml:
return None
return yaml.load(projects_yaml)
def git_clone_and_install(projects_yaml, core_project, depth=1):
""" """
Clone/install all specified OpenStack repositories. Clone/install all specified OpenStack repositories.
@ -510,23 +524,22 @@ def git_clone_and_install(projects_yaml, core_project):
repository: 'git://git.openstack.org/openstack/requirements.git', repository: 'git://git.openstack.org/openstack/requirements.git',
branch: 'stable/icehouse'} branch: 'stable/icehouse'}
directory: /mnt/openstack-git directory: /mnt/openstack-git
http_proxy: http://squid.internal:3128 http_proxy: squid-proxy-url
https_proxy: https://squid.internal:3128 https_proxy: squid-proxy-url
The directory, http_proxy, and https_proxy keys are optional. The directory, http_proxy, and https_proxy keys are optional.
""" """
global requirements_dir global requirements_dir
parent_dir = '/mnt/openstack-git' parent_dir = '/mnt/openstack-git'
http_proxy = None
if not projects_yaml: projects = _git_yaml_load(projects_yaml)
return
projects = yaml.load(projects_yaml)
_git_validate_projects_yaml(projects, core_project) _git_validate_projects_yaml(projects, core_project)
old_environ = dict(os.environ) old_environ = dict(os.environ)
if 'http_proxy' in projects.keys(): if 'http_proxy' in projects.keys():
http_proxy = projects['http_proxy']
os.environ['http_proxy'] = projects['http_proxy'] os.environ['http_proxy'] = projects['http_proxy']
if 'https_proxy' in projects.keys(): if 'https_proxy' in projects.keys():
os.environ['https_proxy'] = projects['https_proxy'] os.environ['https_proxy'] = projects['https_proxy']
@ -534,15 +547,19 @@ def git_clone_and_install(projects_yaml, core_project):
if 'directory' in projects.keys(): if 'directory' in projects.keys():
parent_dir = projects['directory'] parent_dir = projects['directory']
pip_create_virtualenv(os.path.join(parent_dir, 'venv'))
for p in projects['repositories']: for p in projects['repositories']:
repo = p['repository'] repo = p['repository']
branch = p['branch'] branch = p['branch']
if p['name'] == 'requirements': if p['name'] == 'requirements':
repo_dir = _git_clone_and_install_single(repo, branch, parent_dir, repo_dir = _git_clone_and_install_single(repo, branch, depth,
parent_dir, http_proxy,
update_requirements=False) update_requirements=False)
requirements_dir = repo_dir requirements_dir = repo_dir
else: else:
repo_dir = _git_clone_and_install_single(repo, branch, parent_dir, repo_dir = _git_clone_and_install_single(repo, branch, depth,
parent_dir, http_proxy,
update_requirements=True) update_requirements=True)
os.environ = old_environ os.environ = old_environ
@ -574,7 +591,8 @@ def _git_ensure_key_exists(key, keys):
error_out('openstack-origin-git key \'{}\' is missing'.format(key)) error_out('openstack-origin-git key \'{}\' is missing'.format(key))
def _git_clone_and_install_single(repo, branch, parent_dir, update_requirements): def _git_clone_and_install_single(repo, branch, depth, parent_dir, http_proxy,
update_requirements):
""" """
Clone and install a single git repository. Clone and install a single git repository.
""" """
@ -587,7 +605,8 @@ def _git_clone_and_install_single(repo, branch, parent_dir, update_requirements)
if not os.path.exists(dest_dir): if not os.path.exists(dest_dir):
juju_log('Cloning git repo: {}, branch: {}'.format(repo, branch)) juju_log('Cloning git repo: {}, branch: {}'.format(repo, branch))
repo_dir = install_remote(repo, dest=parent_dir, branch=branch) repo_dir = install_remote(repo, dest=parent_dir, branch=branch,
depth=depth)
else: else:
repo_dir = dest_dir repo_dir = dest_dir
@ -598,7 +617,12 @@ def _git_clone_and_install_single(repo, branch, parent_dir, update_requirements)
_git_update_requirements(repo_dir, requirements_dir) _git_update_requirements(repo_dir, requirements_dir)
juju_log('Installing git repo from dir: {}'.format(repo_dir)) juju_log('Installing git repo from dir: {}'.format(repo_dir))
pip_install(repo_dir) if http_proxy:
pip_install(repo_dir, proxy=http_proxy,
venv=os.path.join(parent_dir, 'venv'))
else:
pip_install(repo_dir,
venv=os.path.join(parent_dir, 'venv'))
return repo_dir return repo_dir
@ -621,16 +645,27 @@ def _git_update_requirements(package_dir, reqs_dir):
os.chdir(orig_dir) os.chdir(orig_dir)
def git_pip_venv_dir(projects_yaml):
"""
Return the pip virtualenv path.
"""
parent_dir = '/mnt/openstack-git'
projects = _git_yaml_load(projects_yaml)
if 'directory' in projects.keys():
parent_dir = projects['directory']
return os.path.join(parent_dir, 'venv')
def git_src_dir(projects_yaml, project): def git_src_dir(projects_yaml, project):
""" """
Return the directory where the specified project's source is located. Return the directory where the specified project's source is located.
""" """
parent_dir = '/mnt/openstack-git' parent_dir = '/mnt/openstack-git'
if not projects_yaml: projects = _git_yaml_load(projects_yaml)
return
projects = yaml.load(projects_yaml)
if 'directory' in projects.keys(): if 'directory' in projects.keys():
parent_dir = projects['directory'] parent_dir = projects['directory']
@ -640,3 +675,15 @@ def git_src_dir(projects_yaml, project):
return os.path.join(parent_dir, os.path.basename(p['repository'])) return os.path.join(parent_dir, os.path.basename(p['repository']))
return None return None
def git_yaml_value(projects_yaml, key):
"""
Return the value in projects_yaml for the specified key.
"""
projects = _git_yaml_load(projects_yaml)
if key in projects.keys():
return projects[key]
return None

View File

@ -17,8 +17,11 @@
# You should have received a copy of the GNU Lesser General Public License # You should have received a copy of the GNU Lesser General Public License
# along with charm-helpers. If not, see <http://www.gnu.org/licenses/>. # along with charm-helpers. If not, see <http://www.gnu.org/licenses/>.
import os
import subprocess
from charmhelpers.fetch import apt_install, apt_update from charmhelpers.fetch import apt_install, apt_update
from charmhelpers.core.hookenv import log from charmhelpers.core.hookenv import charm_dir, log
try: try:
from pip import main as pip_execute from pip import main as pip_execute
@ -51,11 +54,15 @@ def pip_install_requirements(requirements, **options):
pip_execute(command) pip_execute(command)
def pip_install(package, fatal=False, upgrade=False, **options): def pip_install(package, fatal=False, upgrade=False, venv=None, **options):
"""Install a python package""" """Install a python package"""
command = ["install"] if venv:
venv_python = os.path.join(venv, 'bin/pip')
command = [venv_python, "install"]
else:
command = ["install"]
available_options = ('proxy', 'src', 'log', "index-url", ) available_options = ('proxy', 'src', 'log', 'index-url', )
for option in parse_options(options, available_options): for option in parse_options(options, available_options):
command.append(option) command.append(option)
@ -69,7 +76,10 @@ def pip_install(package, fatal=False, upgrade=False, **options):
log("Installing {} package with options: {}".format(package, log("Installing {} package with options: {}".format(package,
command)) command))
pip_execute(command) if venv:
subprocess.check_call(command)
else:
pip_execute(command)
def pip_uninstall(package, **options): def pip_uninstall(package, **options):
@ -94,3 +104,16 @@ def pip_list():
"""Returns the list of current python installed packages """Returns the list of current python installed packages
""" """
return pip_execute(["list"]) return pip_execute(["list"])
def pip_create_virtualenv(path=None):
"""Create an isolated Python environment."""
apt_install('python-virtualenv')
if path:
venv_path = path
else:
venv_path = os.path.join(charm_dir(), 'venv')
if not os.path.exists(venv_path):
subprocess.check_call(['virtualenv', venv_path])

View File

@ -28,6 +28,7 @@ import yaml
import subprocess import subprocess
import sys import sys
import errno import errno
import tempfile
from subprocess import CalledProcessError from subprocess import CalledProcessError
import six import six
@ -362,14 +363,34 @@ def relation_set(relation_id=None, relation_settings=None, **kwargs):
"""Set relation information for the current unit""" """Set relation information for the current unit"""
relation_settings = relation_settings if relation_settings else {} relation_settings = relation_settings if relation_settings else {}
relation_cmd_line = ['relation-set'] relation_cmd_line = ['relation-set']
accepts_file = "--file" in subprocess.check_output(
relation_cmd_line + ["--help"], universal_newlines=True)
if relation_id is not None: if relation_id is not None:
relation_cmd_line.extend(('-r', relation_id)) relation_cmd_line.extend(('-r', relation_id))
for k, v in (list(relation_settings.items()) + list(kwargs.items())): settings = relation_settings.copy()
if v is None: settings.update(kwargs)
relation_cmd_line.append('{}='.format(k)) for key, value in settings.items():
else: # Force value to be a string: it always should, but some call
relation_cmd_line.append('{}={}'.format(k, v)) # sites pass in things like dicts or numbers.
subprocess.check_call(relation_cmd_line) if value is not None:
settings[key] = "{}".format(value)
if accepts_file:
# --file was introduced in Juju 1.23.2. Use it by default if
# available, since otherwise we'll break if the relation data is
# too big. Ideally we should tell relation-set to read the data from
# stdin, but that feature is broken in 1.23.2: Bug #1454678.
with tempfile.NamedTemporaryFile(delete=False) as settings_file:
settings_file.write(yaml.safe_dump(settings).encode("utf-8"))
subprocess.check_call(
relation_cmd_line + ["--file", settings_file.name])
os.remove(settings_file.name)
else:
for key, value in settings.items():
if value is None:
relation_cmd_line.append('{}='.format(key))
else:
relation_cmd_line.append('{}={}'.format(key, value))
subprocess.check_call(relation_cmd_line)
# Flush cache of any relation-gets for local unit # Flush cache of any relation-gets for local unit
flush(local_unit()) flush(local_unit())
@ -606,67 +627,6 @@ def charm_dir():
return os.environ.get('CHARM_DIR') return os.environ.get('CHARM_DIR')
def translate_exc(from_exc, to_exc):
def inner_translate_exc1(f):
def inner_translate_exc2(*args, **kwargs):
try:
return f(*args, **kwargs)
except from_exc:
raise to_exc
return inner_translate_exc2
return inner_translate_exc1
@translate_exc(from_exc=OSError, to_exc=NotImplementedError)
def is_leader():
"""Does the current unit hold the juju leadership
Uses juju to determine whether the current unit is the leader of its peers
"""
try:
leader = json.loads(
subprocess.check_output(['is-leader', '--format=json'])
)
return (leader is True)
except ValueError:
raise NotImplementedError
@translate_exc(from_exc=OSError, to_exc=NotImplementedError)
def leader_get(attribute=None):
"""Juju leader get value(s)"""
cmd = ['leader-get', '--format=json'] + [attribute or '-']
try:
ret = json.loads(subprocess.check_output(cmd).decode('UTF-8'))
log("Juju leader-get '%s' = '%s'" % (attribute, ret), level=DEBUG)
return ret
except ValueError:
return None
except CalledProcessError as e:
if e.returncode == 2:
return None
raise
@translate_exc(from_exc=OSError, to_exc=NotImplementedError)
def leader_set(settings=None, **kwargs):
"""Juju leader set value(s)"""
log("Juju leader-set '%s'" % (settings), level=DEBUG)
cmd = ['leader-set']
settings = settings or {}
settings.update(kwargs)
for k, v in settings.iteritems():
if v is None:
cmd.append('{}='.format(k))
else:
cmd.append('{}={}'.format(k, v))
subprocess.check_call(cmd)
@cached @cached
def action_get(key=None): def action_get(key=None):
"""Gets the value of an action parameter, or all key/value param pairs""" """Gets the value of an action parameter, or all key/value param pairs"""
@ -737,3 +697,48 @@ def status_get():
return 'unknown' return 'unknown'
else: else:
raise raise
def translate_exc(from_exc, to_exc):
def inner_translate_exc1(f):
def inner_translate_exc2(*args, **kwargs):
try:
return f(*args, **kwargs)
except from_exc:
raise to_exc
return inner_translate_exc2
return inner_translate_exc1
@translate_exc(from_exc=OSError, to_exc=NotImplementedError)
def is_leader():
"""Does the current unit hold the juju leadership
Uses juju to determine whether the current unit is the leader of its peers
"""
cmd = ['is-leader', '--format=json']
return json.loads(subprocess.check_output(cmd).decode('UTF-8'))
@translate_exc(from_exc=OSError, to_exc=NotImplementedError)
def leader_get(attribute=None):
"""Juju leader get value(s)"""
cmd = ['leader-get', '--format=json'] + [attribute or '-']
return json.loads(subprocess.check_output(cmd).decode('UTF-8'))
@translate_exc(from_exc=OSError, to_exc=NotImplementedError)
def leader_set(settings=None, **kwargs):
"""Juju leader set value(s)"""
log("Juju leader-set '%s'" % (settings), level=DEBUG)
cmd = ['leader-set']
settings = settings or {}
settings.update(kwargs)
for k, v in settings.iteritems():
if v is None:
cmd.append('{}='.format(k))
else:
cmd.append('{}={}'.format(k, v))
subprocess.check_call(cmd)

View File

@ -15,8 +15,8 @@
# along with charm-helpers. If not, see <http://www.gnu.org/licenses/>. # along with charm-helpers. If not, see <http://www.gnu.org/licenses/>.
import os import os
import re
import json import json
from inspect import getargspec
from collections import Iterable, OrderedDict from collections import Iterable, OrderedDict
from charmhelpers.core import host from charmhelpers.core import host
@ -132,8 +132,8 @@ class ServiceManager(object):
if hook_name == 'stop': if hook_name == 'stop':
self.stop_services() self.stop_services()
else: else:
self.provide_data()
self.reconfigure_services() self.reconfigure_services()
self.provide_data()
cfg = hookenv.config() cfg = hookenv.config()
if cfg.implicit_save: if cfg.implicit_save:
cfg.save() cfg.save()
@ -145,15 +145,36 @@ class ServiceManager(object):
A provider must have a `name` attribute, which indicates which relation A provider must have a `name` attribute, which indicates which relation
to set data on, and a `provide_data()` method, which returns a dict of to set data on, and a `provide_data()` method, which returns a dict of
data to set. data to set.
The `provide_data()` method can optionally accept two parameters:
* ``remote_service`` The name of the remote service that the data will
be provided to. The `provide_data()` method will be called once
for each connected service (not unit). This allows the method to
tailor its data to the given service.
* ``service_ready`` Whether or not the service definition had all of
its requirements met, and thus the ``data_ready`` callbacks run.
Note that the ``provided_data`` methods are now called **after** the
``data_ready`` callbacks are run. This gives the ``data_ready`` callbacks
a chance to generate any data necessary for the providing to the remote
services.
""" """
hook_name = hookenv.hook_name() for service_name, service in self.services.items():
for service in self.services.values(): service_ready = self.is_ready(service_name)
for provider in service.get('provided_data', []): for provider in service.get('provided_data', []):
if re.match(r'{}-relation-(joined|changed)'.format(provider.name), hook_name): for relid in hookenv.relation_ids(provider.name):
data = provider.provide_data() units = hookenv.related_units(relid)
_ready = provider._is_ready(data) if hasattr(provider, '_is_ready') else data if not units:
if _ready: continue
hookenv.relation_set(None, data) remote_service = units[0].split('/')[0]
argspec = getargspec(provider.provide_data)
if len(argspec.args) > 1:
data = provider.provide_data(remote_service, service_ready)
else:
data = provider.provide_data()
if data:
hookenv.relation_set(relid, data)
def reconfigure_services(self, *service_names): def reconfigure_services(self, *service_names):
""" """

View File

@ -45,14 +45,16 @@ class GitUrlFetchHandler(BaseFetchHandler):
else: else:
return True return True
def clone(self, source, dest, branch): def clone(self, source, dest, branch, depth=None):
if not self.can_handle(source): if not self.can_handle(source):
raise UnhandledSource("Cannot handle {}".format(source)) raise UnhandledSource("Cannot handle {}".format(source))
repo = Repo.clone_from(source, dest) if depth:
repo.git.checkout(branch) Repo.clone_from(source, dest, branch=branch, depth=depth)
else:
Repo.clone_from(source, dest, branch=branch)
def install(self, source, branch="master", dest=None): def install(self, source, branch="master", dest=None, depth=None):
url_parts = self.parse_url(source) url_parts = self.parse_url(source)
branch_name = url_parts.path.strip("/").split("/")[-1] branch_name = url_parts.path.strip("/").split("/")[-1]
if dest: if dest:
@ -63,7 +65,7 @@ class GitUrlFetchHandler(BaseFetchHandler):
if not os.path.exists(dest_dir): if not os.path.exists(dest_dir):
mkdir(dest_dir, perms=0o755) mkdir(dest_dir, perms=0o755)
try: try:
self.clone(source, dest_dir, branch) self.clone(source, dest_dir, branch, depth)
except GitCommandError as e: except GitCommandError as e:
raise UnhandledSource(e.message) raise UnhandledSource(e.message)
except OSError as e: except OSError as e:

View File

@ -14,6 +14,10 @@ from charmhelpers.fetch import (
apt_install, apt_install,
add_source) add_source)
from charmhelpers.contrib.python.packages import (
pip_install,
)
from charmhelpers.core.hookenv import ( from charmhelpers.core.hookenv import (
charm_dir, charm_dir,
config, config,
@ -47,6 +51,8 @@ from charmhelpers.contrib.openstack.utils import (
git_install_requested, git_install_requested,
git_clone_and_install, git_clone_and_install,
git_src_dir, git_src_dir,
git_yaml_value,
git_pip_venv_dir,
configure_installation_source, configure_installation_source,
os_release, os_release,
) )
@ -64,8 +70,12 @@ PACKAGES = [
"python-psycopg2", "python-keystone", "python-six", "uuid", "haproxy", ] "python-psycopg2", "python-keystone", "python-six", "uuid", "haproxy", ]
BASE_GIT_PACKAGES = [ BASE_GIT_PACKAGES = [
'libffi-dev',
'libmysqlclient-dev',
'libxml2-dev', 'libxml2-dev',
'libxslt1-dev', 'libxslt1-dev',
'libssl-dev',
'libyaml-dev',
'python-dev', 'python-dev',
'python-pip', 'python-pip',
'python-setuptools', 'python-setuptools',
@ -347,6 +357,14 @@ def git_pre_install():
def git_post_install(projects_yaml): def git_post_install(projects_yaml):
"""Perform glance post-install setup.""" """Perform glance post-install setup."""
http_proxy = git_yaml_value(projects_yaml, 'http_proxy')
if http_proxy:
pip_install('mysql-python', proxy=http_proxy,
venv=git_pip_venv_dir(projects_yaml))
else:
pip_install('mysql-python',
venv=git_pip_venv_dir(projects_yaml))
src_etc = os.path.join(git_src_dir(projects_yaml, 'glance'), 'etc') src_etc = os.path.join(git_src_dir(projects_yaml, 'glance'), 'etc')
configs = { configs = {
'src': src_etc, 'src': src_etc,
@ -357,13 +375,34 @@ def git_post_install(projects_yaml):
shutil.rmtree(configs['dest']) shutil.rmtree(configs['dest'])
shutil.copytree(configs['src'], configs['dest']) shutil.copytree(configs['src'], configs['dest'])
symlinks = [
# NOTE(coreycb): Need to find better solution than bin symlinks.
{'src': os.path.join(git_pip_venv_dir(projects_yaml),
'bin/glance-manage'),
'link': '/usr/local/bin/glance-manage'},
# NOTE(coreycb): This is ugly but couldn't find pypi package that
# installs rbd.py and rados.py.
{'src': '/usr/lib/python2.7/dist-packages/rbd.py',
'link': os.path.join(git_pip_venv_dir(projects_yaml),
'lib/python2.7/site-packages/rbd.py')},
{'src': '/usr/lib/python2.7/dist-packages/rados.py',
'link': os.path.join(git_pip_venv_dir(projects_yaml),
'lib/python2.7/site-packages/rados.py')},
]
for s in symlinks:
if os.path.lexists(s['link']):
os.remove(s['link'])
os.symlink(s['src'], s['link'])
bin_dir = os.path.join(git_pip_venv_dir(projects_yaml), 'bin')
glance_api_context = { glance_api_context = {
'service_description': 'Glance API server', 'service_description': 'Glance API server',
'service_name': 'Glance', 'service_name': 'Glance',
'user_name': 'glance', 'user_name': 'glance',
'start_dir': '/var/lib/glance', 'start_dir': '/var/lib/glance',
'process_name': 'glance-api', 'process_name': 'glance-api',
'executable_name': '/usr/local/bin/glance-api', 'executable_name': os.path.join(bin_dir, 'glance-api'),
'config_files': ['/etc/glance/glance-api.conf'], 'config_files': ['/etc/glance/glance-api.conf'],
'log_file': '/var/log/glance/api.log', 'log_file': '/var/log/glance/api.log',
} }
@ -374,7 +413,7 @@ def git_post_install(projects_yaml):
'user_name': 'glance', 'user_name': 'glance',
'start_dir': '/var/lib/glance', 'start_dir': '/var/lib/glance',
'process_name': 'glance-registry', 'process_name': 'glance-registry',
'executable_name': '/usr/local/bin/glance-registry', 'executable_name': os.path.join(bin_dir, 'glance-registry'),
'config_files': ['/etc/glance/glance-registry.conf'], 'config_files': ['/etc/glance/glance-registry.conf'],
'log_file': '/var/log/glance/registry.log', 'log_file': '/var/log/glance/registry.log',
} }

View File

@ -3,16 +3,6 @@ verbose = {{ verbose }}
use_syslog = {{ use_syslog }} use_syslog = {{ use_syslog }}
debug = {{ debug }} debug = {{ debug }}
workers = {{ workers }} workers = {{ workers }}
known_stores = {{ known_stores }}
{% if rbd_pool -%}
default_store = rbd
{% elif swift_store -%}
default_store = swift
{% else -%}
default_store = file
{% endif -%}
bind_host = {{ bind_host }} bind_host = {{ bind_host }}
{% if ext -%} {% if ext -%}
@ -40,6 +30,23 @@ registry_client_protocol = http
notification_driver = rabbit notification_driver = rabbit
{% endif -%} {% endif -%}
delayed_delete = False
scrub_time = 43200
scrubber_datadir = /var/lib/glance/scrubber
image_cache_dir = /var/lib/glance/image-cache/
db_enforce_mysql_charset = False
[glance_store]
filesystem_store_datadir = /var/lib/glance/images/
stores = {{ known_stores }}
{% if rbd_pool -%}
default_store = rbd
{% elif swift_store -%}
default_store = swift
{% else -%}
default_store = file
{% endif -%}
{% if swift_store -%} {% if swift_store -%}
swift_store_auth_version = 2 swift_store_auth_version = 2
swift_store_auth_address = {{ service_protocol }}://{{ service_host }}:{{ service_port }}/v2.0/ swift_store_auth_address = {{ service_protocol }}://{{ service_host }}:{{ service_port }}/v2.0/
@ -59,15 +66,6 @@ rbd_store_pool = {{ rbd_pool }}
rbd_store_chunk_size = 8 rbd_store_chunk_size = 8
{% endif -%} {% endif -%}
delayed_delete = False
scrub_time = 43200
scrubber_datadir = /var/lib/glance/scrubber
image_cache_dir = /var/lib/glance/image-cache/
db_enforce_mysql_charset = False
[glance_store]
filesystem_store_datadir = /var/lib/glance/images/
[image_format] [image_format]
disk_formats=ami,ari,aki,vhd,vmdk,raw,qcow2,vdi,iso,root-tar disk_formats=ami,ari,aki,vhd,vmdk,raw,qcow2,vdi,iso,root-tar

View File

@ -66,10 +66,10 @@ class GlanceBasicDeployment(OpenStackAmuletDeployment):
openstack_origin_git = { openstack_origin_git = {
'repositories': [ 'repositories': [
{'name': 'requirements', {'name': 'requirements',
'repository': 'git://git.openstack.org/openstack/requirements', 'repository': 'git://github.com/openstack/requirements',
'branch': branch}, 'branch': branch},
{'name': 'glance', {'name': 'glance',
'repository': 'git://git.openstack.org/openstack/glance', 'repository': 'git://github.com/openstack/glance',
'branch': branch}, 'branch': branch},
], ],
'directory': '/mnt/openstack-git', 'directory': '/mnt/openstack-git',

View File

@ -131,10 +131,14 @@ class GlanceRelationTests(CharmTestCase):
self.apt_update.assert_called_with(fatal=True) self.apt_update.assert_called_with(fatal=True)
self.apt_install.assert_called_with(['haproxy', 'python-setuptools', self.apt_install.assert_called_with(['haproxy', 'python-setuptools',
'python-six', 'uuid', 'python-six', 'uuid',
'python-mysqldb', 'python-pip', 'python-mysqldb',
'apache2', 'libxslt1-dev', 'libmysqlclient-dev',
'python-psycopg2', 'zlib1g-dev', 'libssl-dev', 'libffi-dev',
'python-dev', 'libxml2-dev'], 'apache2', 'python-pip',
'libxslt1-dev', 'libyaml-dev',
'python-psycopg2',
'zlib1g-dev', 'python-dev',
'libxml2-dev'],
fatal=True) fatal=True)
self.git_install.assert_called_with(projects_yaml) self.git_install.assert_called_with(projects_yaml)

View File

@ -23,6 +23,7 @@ TO_PATCH = [
'apt_install', 'apt_install',
'mkdir', 'mkdir',
'os_release', 'os_release',
'pip_install',
'service_start', 'service_start',
'service_stop', 'service_stop',
'service_name', 'service_name',
@ -236,26 +237,35 @@ class TestGlanceUtils(CharmTestCase):
@patch.object(utils, 'git_src_dir') @patch.object(utils, 'git_src_dir')
@patch.object(utils, 'service_restart') @patch.object(utils, 'service_restart')
@patch.object(utils, 'render') @patch.object(utils, 'render')
@patch.object(utils, 'git_pip_venv_dir')
@patch('os.path.join') @patch('os.path.join')
@patch('os.path.exists') @patch('os.path.exists')
@patch('os.symlink')
@patch('shutil.copytree') @patch('shutil.copytree')
@patch('shutil.rmtree') @patch('shutil.rmtree')
def test_git_post_install(self, rmtree, copytree, exists, join, render, @patch('subprocess.check_call')
service_restart, git_src_dir): def test_git_post_install(self, check_call, rmtree, copytree, symlink,
exists, join, venv, render, service_restart,
git_src_dir):
projects_yaml = openstack_origin_git projects_yaml = openstack_origin_git
join.return_value = 'joined-string' join.return_value = 'joined-string'
venv.return_value = '/mnt/openstack-git/venv'
utils.git_post_install(projects_yaml) utils.git_post_install(projects_yaml)
expected = [ expected = [
call('joined-string', '/etc/glance'), call('joined-string', '/etc/glance'),
] ]
copytree.assert_has_calls(expected) copytree.assert_has_calls(expected)
expected = [
call('joined-string', '/usr/local/bin/glance-manage'),
]
symlink.assert_has_calls(expected, any_order=True)
glance_api_context = { glance_api_context = {
'service_description': 'Glance API server', 'service_description': 'Glance API server',
'service_name': 'Glance', 'service_name': 'Glance',
'user_name': 'glance', 'user_name': 'glance',
'start_dir': '/var/lib/glance', 'start_dir': '/var/lib/glance',
'process_name': 'glance-api', 'process_name': 'glance-api',
'executable_name': '/usr/local/bin/glance-api', 'executable_name': 'joined-string',
'config_files': ['/etc/glance/glance-api.conf'], 'config_files': ['/etc/glance/glance-api.conf'],
'log_file': '/var/log/glance/api.log', 'log_file': '/var/log/glance/api.log',
} }
@ -265,7 +275,7 @@ class TestGlanceUtils(CharmTestCase):
'user_name': 'glance', 'user_name': 'glance',
'start_dir': '/var/lib/glance', 'start_dir': '/var/lib/glance',
'process_name': 'glance-registry', 'process_name': 'glance-registry',
'executable_name': '/usr/local/bin/glance-registry', 'executable_name': 'joined-string',
'config_files': ['/etc/glance/glance-registry.conf'], 'config_files': ['/etc/glance/glance-registry.conf'],
'log_file': '/var/log/glance/registry.log', 'log_file': '/var/log/glance/registry.log',
} }