Deploy from source

This commit is contained in:
Corey Bryant 2015-04-15 14:17:56 +00:00
commit 709209244d
34 changed files with 1032 additions and 82 deletions

View File

@ -1,2 +1,3 @@
bin
.coverage
tags

View File

@ -2,7 +2,7 @@
PYTHON := /usr/bin/env python
lint:
@flake8 --exclude hooks/charmhelpers hooks unit_tests tests
@flake8 --exclude hooks/charmhelpers actions hooks unit_tests tests
@charm proof
unit_test:
@ -18,8 +18,10 @@ test:
# coreycb note: The -v should only be temporary until Amulet sends
# raise_status() messages to stderr:
# https://bugs.launchpad.net/amulet/+bug/1320357
@juju test -v -p AMULET_HTTP_PROXY --timeout 900 \
00-setup 14-basic-precise-icehouse 15-basic-trusty-icehouse
@juju test -v -p AMULET_HTTP_PROXY --timeout 1200 \
00-setup 14-basic-precise-icehouse 15-basic-trusty-icehouse \
16-basic-trusty-icehouse-git 17-basic-trusty-juno \
18-basic-trusty-juno-git
sync: bin/charm_helpers_sync.py
@$(PYTHON) bin/charm_helpers_sync.py -c charm-helpers-hooks.yaml

View File

@ -23,3 +23,88 @@ juju deploy postgresql
juju add-relation "nova-cloud-controller:pgsql-nova-db" "postgresql:db"
juju add-relation "nova-cloud-controller:pgsql-neutron-db" "postgresql:db"
Deploying from source
=====================
The minimum openstack-origin-git config required to deploy from source is:
openstack-origin-git:
"repositories:
- {name: requirements,
repository: 'git://git.openstack.org/openstack/requirements',
branch: stable/juno}
- {name: nova,
repository: 'git://git.openstack.org/openstack/nova',
branch: stable/juno}"
Note that there are only two 'name' values the charm knows about: 'requirements'
and 'nova'. These repositories must correspond to these 'name' values.
Additionally, the requirements repository must be specified first and the
nova repository must be specified last. All other repostories are installed
in the order in which they are specified.
The following is a full list of current tip repos (may not be up-to-date):
openstack-origin-git:
"repositories:
- {name: requirements,
repository: 'git://git.openstack.org/openstack/requirements',
branch: master}
- {name: oslo-concurrency,
repository: 'git://git.openstack.org/openstack/oslo.concurrency',
branch: master}
- {name: oslo-config,
repository: 'git://git.openstack.org/openstack/oslo.config',
branch: master}
- {name: oslo-context,
repository: 'git://git.openstack.org/openstack/oslo.context.git',
branch: master}
- {name: oslo-db,
repository: 'git://git.openstack.org/openstack/oslo.db',
branch: master}
- {name: oslo-i18n,
repository: 'git://git.openstack.org/openstack/oslo.i18n',
branch: master}
- {name: oslo-log,
repository: 'git://git.openstack.org/openstack/oslo.log',
branch: master}
- {name: oslo-messaging,
repository: 'git://git.openstack.org/openstack/oslo.messaging.git',
branch: master}
- {name: oslo-middleware,
repository': 'git://git.openstack.org/openstack/oslo.middleware.git',
branch: master}
- {name: oslo-rootwrap',
repository: 'git://git.openstack.org/openstack/oslo.rootwrap.git',
branch: master}
- {name: oslo-serialization,
repository: 'git://git.openstack.org/openstack/oslo.serialization',
branch: master}
- {name: oslo-utils,
repository: 'git://git.openstack.org/openstack/oslo.utils',
branch: master}
- {name: pbr,
repository: 'git://git.openstack.org/openstack-dev/pbr',
branch: master}
- {name: stevedore,
repository: 'git://git.openstack.org/openstack/stevedore.git',
branch: 'master'}
- {name: sqlalchemy-migrate,
repository: 'git://git.openstack.org/stackforge/sqlalchemy-migrate',
branch: master}
- {name: python-cinderclient,
repository: 'git://git.openstack.org/openstack/python-cinderclient.git',
branch: master}
- {name: python-glanceclient,
repository': 'git://git.openstack.org/openstack/python-glanceclient.git',
branch: master}
- {name: python-neutronlient,
repository': 'git://git.openstack.org/openstack/python-neutronclient.git',
branch: master}
- {name: keystonemiddleware,
repository: 'git://git.openstack.org/openstack/keystonemiddleware',
branch: master}
- {name: nova,
repository: 'git://git.openstack.org/openstack/nova',
branch: master}"

2
actions.yaml Normal file
View File

@ -0,0 +1,2 @@
git-reinstall:
description: Reinstall nova-cloud-controller from the openstack-origin-git repositories.

1
actions/git-reinstall Symbolic link
View File

@ -0,0 +1 @@
git_reinstall.py

45
actions/git_reinstall.py Executable file
View File

@ -0,0 +1,45 @@
#!/usr/bin/python
import sys
import traceback
sys.path.append('hooks/')
from charmhelpers.contrib.openstack.utils import (
git_install_requested,
)
from charmhelpers.core.hookenv import (
action_set,
action_fail,
config,
)
from nova_cc_utils import (
git_install,
)
from nova_cc_hooks import (
config_changed,
)
def git_reinstall():
"""Reinstall from source and restart services.
If the openstack-origin-git config option was used to install openstack
from source git repositories, then this action can be used to reinstall
from updated git repositories, followed by a restart of services."""
if not git_install_requested():
action_fail('openstack-origin-git is not configured')
return
try:
git_install(config('openstack-origin-git'))
except:
action_set({'traceback': traceback.format_exc()})
action_fail('git-reinstall resulted in an unexpected error')
if __name__ == '__main__':
git_reinstall()
config_changed()

View File

@ -14,6 +14,22 @@ options:
Note that updating this setting to a source that is known to
provide a later version of OpenStack will trigger a software
upgrade.
Note that when openstack-origin-git is specified, openstack
specific packages will be installed from source rather than
from the openstack-origin repository.
openstack-origin-git:
default:
type: string
description: |
Specifies a YAML-formatted dictionary listing the git
repositories and branches from which to install OpenStack and
its dependencies.
Note that the installed config files will be determined based on
the OpenStack release of the openstack-origin option.
For more details see README.md.
rabbit-user:
default: nova
type: string

View File

@ -320,14 +320,15 @@ def db_ssl(rdata, ctxt, ssl_dir):
class IdentityServiceContext(OSContextGenerator):
interfaces = ['identity-service']
def __init__(self, service=None, service_user=None):
def __init__(self, service=None, service_user=None, rel_name='identity-service'):
self.service = service
self.service_user = service_user
self.rel_name = rel_name
self.interfaces = [self.rel_name]
def __call__(self):
log('Generating template context for identity-service', level=DEBUG)
log('Generating template context for ' + self.rel_name, level=DEBUG)
ctxt = {}
if self.service and self.service_user:
@ -341,7 +342,7 @@ class IdentityServiceContext(OSContextGenerator):
ctxt['signing_dir'] = cachedir
for rid in relation_ids('identity-service'):
for rid in relation_ids(self.rel_name):
for unit in related_units(rid):
rdata = relation_get(rid=rid, unit=unit)
serv_host = rdata.get('service_host')
@ -807,6 +808,19 @@ class NeutronContext(OSContextGenerator):
return ovs_ctxt
def nuage_ctxt(self):
driver = neutron_plugin_attribute(self.plugin, 'driver',
self.network_manager)
config = neutron_plugin_attribute(self.plugin, 'config',
self.network_manager)
nuage_ctxt = {'core_plugin': driver,
'neutron_plugin': 'vsp',
'neutron_security_groups': self.neutron_security_groups,
'local_ip': unit_private_ip(),
'config': config}
return nuage_ctxt
def nvp_ctxt(self):
driver = neutron_plugin_attribute(self.plugin, 'driver',
self.network_manager)
@ -890,6 +904,8 @@ class NeutronContext(OSContextGenerator):
ctxt.update(self.n1kv_ctxt())
elif self.plugin == 'Calico':
ctxt.update(self.calico_ctxt())
elif self.plugin == 'vsp':
ctxt.update(self.nuage_ctxt())
alchemy_flags = config('neutron-alchemy-flags')
if alchemy_flags:

View File

@ -180,6 +180,19 @@ def neutron_plugins():
'nova-api-metadata']],
'server_packages': ['neutron-server', 'calico-control'],
'server_services': ['neutron-server']
},
'vsp': {
'config': '/etc/neutron/plugins/nuage/nuage_plugin.ini',
'driver': 'neutron.plugins.nuage.plugin.NuagePlugin',
'contexts': [
context.SharedDBContext(user=config('neutron-database-user'),
database=config('neutron-database'),
relation_prefix='neutron',
ssl_dir=NEUTRON_CONF_DIR)],
'services': [],
'packages': [],
'server_packages': ['neutron-server', 'neutron-plugin-nuage'],
'server_services': ['neutron-server']
}
}
if release >= 'icehouse':

View File

@ -9,5 +9,9 @@ respawn
exec start-stop-daemon --start --chuid {{ user_name }} \
--chdir {{ start_dir }} --name {{ process_name }} \
--exec {{ executable_name }} -- \
{% for config_file in config_files -%}
--config-file={{ config_file }} \
{% endfor -%}
{% if log_file -%}
--log-file={{ log_file }}
{% endif -%}

View File

@ -510,8 +510,10 @@ def git_clone_and_install(projects_yaml, core_project):
repository: 'git://git.openstack.org/openstack/requirements.git',
branch: 'stable/icehouse'}
directory: /mnt/openstack-git
http_proxy: http://squid.internal:3128
https_proxy: https://squid.internal:3128
The directory key is optional.
The directory, http_proxy, and https_proxy keys are optional.
"""
global requirements_dir
parent_dir = '/mnt/openstack-git'
@ -522,6 +524,12 @@ def git_clone_and_install(projects_yaml, core_project):
projects = yaml.load(projects_yaml)
_git_validate_projects_yaml(projects, core_project)
if 'http_proxy' in projects.keys():
os.environ['http_proxy'] = projects['http_proxy']
if 'https_proxy' in projects.keys():
os.environ['https_proxy'] = projects['https_proxy']
if 'directory' in projects.keys():
parent_dir = projects['directory']

View File

@ -20,11 +20,13 @@
# Authors:
# Charm Helpers Developers <juju@lists.ubuntu.com>
from __future__ import print_function
import os
import json
import yaml
import subprocess
import sys
import errno
from subprocess import CalledProcessError
import six
@ -87,7 +89,18 @@ def log(message, level=None):
if not isinstance(message, six.string_types):
message = repr(message)
command += [message]
subprocess.call(command)
# Missing juju-log should not cause failures in unit tests
# Send log output to stderr
try:
subprocess.call(command)
except OSError as e:
if e.errno == errno.ENOENT:
if level:
message = "{}: {}".format(level, message)
message = "juju-log: {}".format(message)
print(message, file=sys.stderr)
else:
raise
class Serializable(UserDict):

View File

@ -33,9 +33,9 @@ def bool_from_string(value):
value = value.strip().lower()
if value in ['y', 'yes', 'true', 't']:
if value in ['y', 'yes', 'true', 't', 'on']:
return True
elif value in ['n', 'no', 'false', 'f']:
elif value in ['n', 'no', 'false', 'f', 'off']:
return False
msg = "Unable to interpret string value '%s' as boolean" % (value)

View File

@ -1,3 +1,5 @@
import os
from charmhelpers.core.hookenv import (
config,
relation_ids,
@ -329,4 +331,22 @@ class InstanceConsoleContext(context.OSContextGenerator):
servers = []
ctxt['memcached_servers'] = ','.join(servers)
# Configure nova-novncproxy https if nova-api is using https.
if https():
cn = resolve_address(endpoint_type=INTERNAL)
if cn:
cert_filename = 'cert_{}'.format(cn)
key_filename = 'key_{}'.format(cn)
else:
cert_filename = 'cert'
key_filename = 'key'
ssl_dir = '/etc/apache2/ssl/nova'
cert = os.path.join(ssl_dir, cert_filename)
key = os.path.join(ssl_dir, key_filename)
if os.path.exists(cert) and os.path.exists(key):
ctxt['ssl_cert'] = cert
ctxt['ssl_key'] = key
return ctxt

View File

@ -43,7 +43,9 @@ from charmhelpers.fetch import (
)
from charmhelpers.contrib.openstack.utils import (
config_value_changed,
configure_installation_source,
git_install_requested,
openstack_upgrade_available,
os_release,
os_requires_version,
@ -75,6 +77,7 @@ from nova_cc_utils import (
disable_services,
do_openstack_upgrade,
enable_services,
git_install,
keystone_ca_cert_b64,
migrate_neutron_database,
migrate_nova_database,
@ -132,9 +135,12 @@ CONFIGS = register_configs()
def install():
execd_preinstall()
configure_installation_source(config('openstack-origin'))
apt_update()
apt_install(determine_packages(), fatal=True)
git_install(config('openstack-origin-git'))
_files = os.path.join(charm_dir(), 'files')
if os.path.isdir(_files):
for f in os.listdir(_files):
@ -160,16 +166,21 @@ def config_changed():
relation_prefix='nova')
global CONFIGS
if openstack_upgrade_available('nova-common'):
CONFIGS = do_openstack_upgrade()
[neutron_api_relation_joined(rid=rid, remote_restart=True)
for rid in relation_ids('neutron-api')]
if git_install_requested():
if config_value_changed('openstack-origin-git'):
git_install(config('openstack-origin-git'))
else:
if openstack_upgrade_available('nova-common'):
CONFIGS = do_openstack_upgrade()
[neutron_api_relation_joined(rid=rid, remote_restart=True)
for rid in relation_ids('neutron-api')]
save_script_rc()
configure_https()
CONFIGS.write_all()
if console_attributes('protocol'):
apt_update()
apt_install(console_attributes('packages'), fatal=True)
if not git_install_requested():
apt_update()
apt_install(console_attributes('packages'), fatal=True)
[compute_joined(rid=rid)
for rid in relation_ids('cloud-compute')]
for r_id in relation_ids('identity-service'):

View File

@ -1,4 +1,5 @@
import os
import shutil
import subprocess
import ConfigParser
@ -19,6 +20,9 @@ from charmhelpers.contrib.openstack.utils import (
get_host_ip,
get_hostname,
get_os_codename_install_source,
git_install_requested,
git_clone_and_install,
git_src_dir,
is_ip,
os_release,
save_script_rc as _save_script_rc)
@ -31,6 +35,7 @@ from charmhelpers.fetch import (
)
from charmhelpers.core.hookenv import (
charm_dir,
config,
log,
relation_get,
@ -42,13 +47,19 @@ from charmhelpers.core.hookenv import (
)
from charmhelpers.core.host import (
adduser,
add_group,
add_user_to_group,
mkdir,
service,
service_start,
service_stop,
service_running,
lsb_release
lsb_release,
)
from charmhelpers.core.templating import render
from charmhelpers.contrib.network.ip import (
is_ipv6
)
@ -72,6 +83,40 @@ BASE_PACKAGES = [
'python-memcache',
]
BASE_GIT_PACKAGES = [
'libxml2-dev',
'libxslt1-dev',
'python-dev',
'python-pip',
'python-setuptools',
'zlib1g-dev',
]
LATE_GIT_PACKAGES = [
'novnc',
'spice-html5',
'websockify',
]
# ubuntu packages that should not be installed when deploying from git
GIT_PACKAGE_BLACKLIST = [
'neutron-server',
'nova-api-ec2',
'nova-api-os-compute',
'nova-api-os-volume',
'nova-cert',
'nova-conductor',
'nova-consoleauth',
'nova-novncproxy',
'nova-objectstore',
'nova-scheduler',
'nova-spiceproxy',
'nova-xvpvncproxy',
'python-keystoneclient',
'python-six',
'quantum-server',
]
BASE_SERVICES = [
'nova-api-ec2',
'nova-api-os-compute',
@ -377,6 +422,15 @@ def determine_packages():
packages.extend(pkgs)
if console_attributes('packages'):
packages.extend(console_attributes('packages'))
if git_install_requested():
packages = list(set(packages))
packages.extend(BASE_GIT_PACKAGES)
# don't include packages that will be installed from git
for p in GIT_PACKAGE_BLACKLIST:
if p in packages:
packages.remove(p)
return list(set(packages))
@ -974,3 +1028,210 @@ def setup_ipv6():
' main')
apt_update()
apt_install('haproxy/trusty-backports', fatal=True)
def git_install(projects_yaml):
"""Perform setup, and install git repos specified in yaml parameter."""
if git_install_requested():
git_pre_install()
git_clone_and_install(projects_yaml, core_project='nova')
git_post_install(projects_yaml)
def git_pre_install():
"""Perform pre-install setup."""
dirs = [
'/var/lib/nova',
'/var/lib/nova/buckets',
'/var/lib/nova/CA',
'/var/lib/nova/CA/INTER',
'/var/lib/nova/CA/newcerts',
'/var/lib/nova/CA/private',
'/var/lib/nova/CA/reqs',
'/var/lib/nova/images',
'/var/lib/nova/instances',
'/var/lib/nova/keys',
'/var/lib/nova/networks',
'/var/lib/nova/tmp',
'/var/log/nova',
]
adduser('nova', shell='/bin/bash', system_user=True)
subprocess.check_call(['usermod', '--home', '/var/lib/nova', 'nova'])
add_group('nova', system_group=True)
add_user_to_group('nova', 'nova')
for d in dirs:
mkdir(d, owner='nova', group='nova', perms=0755, force=False)
def git_post_install(projects_yaml):
"""Perform post-install setup."""
src_etc = os.path.join(git_src_dir(projects_yaml, 'nova'), 'etc/nova')
configs = [
{'src': src_etc,
'dest': '/etc/nova'},
]
for c in configs:
if os.path.exists(c['dest']):
shutil.rmtree(c['dest'])
shutil.copytree(c['src'], c['dest'])
render('git/nova_sudoers', '/etc/sudoers.d/nova_sudoers', {}, perms=0o440)
nova_cc = 'nova-cloud-controller'
nova_user = 'nova'
start_dir = '/var/lib/nova'
nova_conf = '/etc/nova/nova.conf'
nova_ec2_api_context = {
'service_description': 'Nova EC2 API server',
'service_name': nova_cc,
'user_name': nova_user,
'start_dir': start_dir,
'process_name': 'nova-api-ec2',
'executable_name': '/usr/local/bin/nova-api-ec2',
'config_files': [nova_conf],
}
nova_api_os_compute_context = {
'service_description': 'Nova OpenStack Compute API server',
'service_name': nova_cc,
'user_name': nova_user,
'start_dir': start_dir,
'process_name': 'nova-api-os-compute',
'executable_name': '/usr/local/bin/nova-api-os-compute',
'config_files': [nova_conf],
}
nova_cells_context = {
'service_description': 'Nova cells',
'service_name': nova_cc,
'user_name': nova_user,
'start_dir': start_dir,
'process_name': 'nova-cells',
'executable_name': '/usr/local/bin/nova-cells',
'config_files': [nova_conf],
}
nova_cert_context = {
'service_description': 'Nova cert',
'service_name': nova_cc,
'user_name': nova_user,
'start_dir': start_dir,
'process_name': 'nova-cert',
'executable_name': '/usr/local/bin/nova-cert',
'config_files': [nova_conf],
}
nova_conductor_context = {
'service_description': 'Nova conductor',
'service_name': nova_cc,
'user_name': nova_user,
'start_dir': start_dir,
'process_name': 'nova-conductor',
'executable_name': '/usr/local/bin/nova-conductor',
'config_files': [nova_conf],
}
nova_consoleauth_context = {
'service_description': 'Nova console auth',
'service_name': nova_cc,
'user_name': nova_user,
'start_dir': start_dir,
'process_name': 'nova-consoleauth',
'executable_name': '/usr/local/bin/nova-consoleauth',
'config_files': [nova_conf],
}
nova_console_context = {
'service_description': 'Nova console',
'service_name': nova_cc,
'user_name': nova_user,
'start_dir': start_dir,
'process_name': 'nova-console',
'executable_name': '/usr/local/bin/nova-console',
'config_files': [nova_conf],
}
nova_novncproxy_context = {
'service_description': 'Nova NoVNC proxy',
'service_name': nova_cc,
'user_name': nova_user,
'start_dir': start_dir,
'process_name': 'nova-novncproxy',
'executable_name': '/usr/local/bin/nova-novncproxy',
'config_files': [nova_conf],
}
nova_objectstore_context = {
'service_description': 'Nova object store',
'service_name': nova_cc,
'user_name': nova_user,
'start_dir': start_dir,
'process_name': 'nova-objectstore',
'executable_name': '/usr/local/bin/nova-objectstore',
'config_files': [nova_conf],
}
nova_scheduler_context = {
'service_description': 'Nova scheduler',
'service_name': nova_cc,
'user_name': nova_user,
'start_dir': start_dir,
'process_name': 'nova-scheduler',
'executable_name': '/usr/local/bin/nova-scheduler',
'config_files': [nova_conf],
}
nova_spiceproxy_context = {
'service_description': 'Nova spice proxy',
'service_name': nova_cc,
'user_name': nova_user,
'start_dir': start_dir,
'process_name': 'nova-spicehtml5proxy',
'executable_name': '/usr/local/bin/nova-spicehtml5proxy',
'config_files': [nova_conf],
}
nova_xvpvncproxy_context = {
'service_description': 'Nova XVPVNC proxy',
'service_name': nova_cc,
'user_name': nova_user,
'start_dir': start_dir,
'process_name': 'nova-xvpvncproxy',
'executable_name': '/usr/local/bin/nova-xvpvncproxy',
'config_files': [nova_conf],
}
# NOTE(coreycb): Needs systemd support
templates_dir = 'hooks/charmhelpers/contrib/openstack/templates'
templates_dir = os.path.join(charm_dir(), templates_dir)
render('git.upstart', '/etc/init/nova-api-ec2.conf',
nova_ec2_api_context, perms=0o644,
templates_dir=templates_dir)
render('git.upstart', '/etc/init/nova-api-os-compute.conf',
nova_api_os_compute_context, perms=0o644,
templates_dir=templates_dir)
render('git.upstart', '/etc/init/nova-cells.conf',
nova_cells_context, perms=0o644,
templates_dir=templates_dir)
render('git.upstart', '/etc/init/nova-cert.conf',
nova_cert_context, perms=0o644,
templates_dir=templates_dir)
render('git.upstart', '/etc/init/nova-conductor.conf',
nova_conductor_context, perms=0o644,
templates_dir=templates_dir)
render('git.upstart', '/etc/init/nova-consoleauth.conf',
nova_consoleauth_context, perms=0o644,
templates_dir=templates_dir)
render('git.upstart', '/etc/init/nova-console.conf',
nova_console_context, perms=0o644,
templates_dir=templates_dir)
render('git.upstart', '/etc/init/nova-novncproxy.conf',
nova_novncproxy_context, perms=0o644,
templates_dir=templates_dir)
render('git.upstart', '/etc/init/nova-objectstore.conf',
nova_objectstore_context, perms=0o644,
templates_dir=templates_dir)
render('git.upstart', '/etc/init/nova-scheduler.conf',
nova_scheduler_context, perms=0o644,
templates_dir=templates_dir)
render('git.upstart', '/etc/init/nova-spiceproxy.conf',
nova_spiceproxy_context, perms=0o644,
templates_dir=templates_dir)
render('git.upstart', '/etc/init/nova-xvpvncproxy.conf',
nova_xvpvncproxy_context, perms=0o644,
templates_dir=templates_dir)
apt_update()
apt_install(LATE_GIT_PACKAGES, fatal=True)

View File

@ -0,0 +1,4 @@
Defaults:nova !requiretty
nova ALL = (root) NOPASSWD: /usr/local/bin/nova-rootwrap /etc/nova/rootwrap.conf *

View File

@ -42,6 +42,8 @@ my_ip = {{ host_ip }}
memcached_servers = {{ memcached_servers }}
{% endif %}
{% include "parts/novnc" %}
{% if keystone_ec2_url -%}
keystone_ec2_url = {{ keystone_ec2_url }}
{% endif -%}
@ -70,6 +72,10 @@ default_floating_pool = {{ external_network }}
{% endif -%}
{% endif -%}
{% if neutron_plugin and neutron_plugin == 'vsp' -%}
neutron_ovs_bridge = alubr0
{% endif -%}
{% if neutron_plugin and neutron_plugin == 'nvp' -%}
security_group_api = neutron
nova_firewall_driver = nova.virt.firewall.NoopFirewallDriver

View File

@ -42,6 +42,8 @@ my_ip = {{ host_ip }}
memcached_servers = {{ memcached_servers }}
{% endif %}
{% include "parts/novnc" %}
{% if keystone_ec2_url -%}
keystone_ec2_url = {{ keystone_ec2_url }}
{% endif -%}
@ -70,6 +72,10 @@ default_floating_pool = {{ external_network }}
{% endif -%}
{% endif -%}
{% if neutron_plugin and neutron_plugin == 'vsp' -%}
neutron_ovs_bridge = alubr0
{% endif -%}
{% if neutron_plugin and neutron_plugin == 'nvp' -%}
security_group_api = neutron
nova_firewall_driver = nova.virt.firewall.NoopFirewallDriver

View File

@ -41,6 +41,8 @@ my_ip = {{ host_ip }}
memcached_servers = {{ memcached_servers }}
{% endif %}
{% include "parts/novnc" %}
{% if keystone_ec2_url -%}
keystone_ec2_url = {{ keystone_ec2_url }}
{% endif -%}
@ -63,6 +65,10 @@ default_floating_pool = {{ external_network }}
{% endif -%}
{% endif -%}
{% if neutron_plugin and neutron_plugin == 'vsp' -%}
neutron_ovs_bridge = alubr0
{% endif -%}
{% if neutron_plugin and neutron_plugin == 'nvp' -%}
security_group_api = neutron
nova_firewall_driver = nova.virt.firewall.NoopFirewallDriver

9
templates/parts/novnc Normal file
View File

@ -0,0 +1,9 @@
{%- if ssl_only -%}
ssl_only=true
{% endif -%}
{% if ssl_cert -%}
cert={{ ssl_cert }}
{% endif -%}
{% if ssl_key -%}
key={{ ssl_key }}
{% endif %}

View File

@ -1,10 +0,0 @@
#!/usr/bin/python
"""Amulet tests on a basic nova cloud controller deployment on
precise-essex."""
from basic_deployment import NovaCCBasicDeployment
if __name__ == '__main__':
deployment = NovaCCBasicDeployment(series='precise')
deployment.run_tests()

View File

@ -1,18 +0,0 @@
#!/usr/bin/python
"""Amulet tests on a basic nova cloud controller deployment on
precise-folsom."""
import amulet
from basic_deployment import NovaCCBasicDeployment
if __name__ == '__main__':
# NOTE(coreycb): Skipping failing test until resolved. 'nova-manage db sync'
# fails in shared-db-relation-changed (only fails on folsom)
message = "Skipping failing test until resolved"
amulet.raise_status(amulet.SKIP, msg=message)
deployment = NovaCCBasicDeployment(series='precise',
openstack='cloud:precise-folsom',
source='cloud:precise-updates/folsom')
deployment.run_tests()

View File

@ -1,12 +0,0 @@
#!/usr/bin/python
"""Amulet tests on a basic nova cloud controller deployment on
precise-grizzly."""
from basic_deployment import NovaCCBasicDeployment
if __name__ == '__main__':
deployment = NovaCCBasicDeployment(series='precise',
openstack='cloud:precise-grizzly',
source='cloud:precise-updates/grizzly')
deployment.run_tests()

View File

@ -1,12 +0,0 @@
#!/usr/bin/python
"""Amulet tests on a basic nova cloud controller deployment on
precise-havana."""
from basic_deployment import NovaCCBasicDeployment
if __name__ == '__main__':
deployment = NovaCCBasicDeployment(series='precise',
openstack='cloud:precise-havana',
source='cloud:precise-updates/havana')
deployment.run_tests()

View File

@ -0,0 +1,10 @@
#!/usr/bin/python
"""Amulet tests on a basic nova cloud controller git deployment on
trusty-icehouse."""
from basic_deployment import NovaCCBasicDeployment
if __name__ == '__main__':
deployment = NovaCCBasicDeployment(series='trusty', git=True)
deployment.run_tests()

12
tests/17-basic-trusty-juno Executable file
View File

@ -0,0 +1,12 @@
#!/usr/bin/python
"""Amulet tests on a basic nova cloud controller deployment on
trusty-juno."""
from basic_deployment import NovaCCBasicDeployment
if __name__ == '__main__':
deployment = NovaCCBasicDeployment(series='trusty',
openstack='cloud:trusty-juno',
source='cloud:trusty-updates/juno')
deployment.run_tests()

13
tests/18-basic-trusty-juno-git Executable file
View File

@ -0,0 +1,13 @@
#!/usr/bin/python
"""Amulet tests on a basic nova cloud controller git deployment on
trusty-juno."""
from basic_deployment import NovaCCBasicDeployment
if __name__ == '__main__':
deployment = NovaCCBasicDeployment(series='trusty',
openstack='cloud:trusty-juno',
source='cloud:trusty-updates/juno',
git=True)
deployment.run_tests()

View File

@ -1,6 +1,8 @@
#!/usr/bin/python
import amulet
import os
import yaml
from charmhelpers.contrib.openstack.amulet.deployment import (
OpenStackAmuletDeployment
@ -13,15 +15,17 @@ from charmhelpers.contrib.openstack.amulet.utils import (
)
# Use DEBUG to turn on debug logging
u = OpenStackAmuletUtils(ERROR)
u = OpenStackAmuletUtils(DEBUG)
class NovaCCBasicDeployment(OpenStackAmuletDeployment):
"""Amulet tests on a basic nova cloud controller deployment."""
def __init__(self, series=None, openstack=None, source=None, stable=True):
def __init__(self, series=None, openstack=None, source=None, git=False,
stable=False):
"""Deploy the entire test environment."""
super(NovaCCBasicDeployment, self).__init__(series, openstack, source, stable)
self.git = git
self._add_services()
self._add_relations()
self._configure_services()
@ -62,9 +66,28 @@ class NovaCCBasicDeployment(OpenStackAmuletDeployment):
def _configure_services(self):
"""Configure all of the services."""
nova_cc_config = {}
if self.git:
branch = 'stable/' + self._get_openstack_release_string()
amulet_http_proxy = os.environ.get('AMULET_HTTP_PROXY')
openstack_origin_git = {
'repositories': [
{'name': 'requirements',
'repository': 'git://git.openstack.org/openstack/requirements',
'branch': branch},
{'name': 'nova',
'repository': 'git://git.openstack.org/openstack/nova',
'branch': branch},
],
'directory': '/mnt/openstack-git',
'http_proxy': amulet_http_proxy,
'https_proxy': amulet_http_proxy,
}
nova_cc_config['openstack-origin-git'] = yaml.dump(openstack_origin_git)
keystone_config = {'admin-password': 'openstack',
'admin-token': 'ubuntutesting'}
configs = {'keystone': keystone_config}
configs = {'nova-cloud-controller': nova_cc_config,
'keystone': keystone_config}
super(NovaCCBasicDeployment, self)._configure_services(configs)
def _initialize_tests(self):

View File

@ -1,2 +1,4 @@
import sys
sys.path.append('actions/')
sys.path.append('hooks/')

View File

@ -0,0 +1,98 @@
from mock import patch, MagicMock
with patch('charmhelpers.core.hookenv.config') as config:
config.return_value = 'nova'
import nova_cc_utils as utils # noqa
_reg = utils.register_configs
_map = utils.restart_map
utils.register_configs = MagicMock()
utils.restart_map = MagicMock()
with patch('nova_cc_utils.guard_map') as gmap:
with patch('charmhelpers.core.hookenv.config') as config:
config.return_value = False
gmap.return_value = {}
import git_reinstall
utils.register_configs = _reg
utils.restart_map = _map
from test_utils import (
CharmTestCase
)
TO_PATCH = [
'config',
]
openstack_origin_git = \
"""repositories:
- {name: requirements,
repository: 'git://git.openstack.org/openstack/requirements',
branch: stable/juno}
- {name: nova,
repository: 'git://git.openstack.org/openstack/nova',
branch: stable/juno}"""
class TestnovaAPIActions(CharmTestCase):
def setUp(self):
super(TestnovaAPIActions, self).setUp(git_reinstall, TO_PATCH)
self.config.side_effect = self.test_config.get
@patch.object(git_reinstall, 'action_set')
@patch.object(git_reinstall, 'action_fail')
@patch.object(git_reinstall, 'git_install')
def test_git_reinstall(self, git_install, action_fail, action_set):
self.test_config.set('openstack-origin-git', openstack_origin_git)
git_reinstall.git_reinstall()
git_install.assert_called_with(openstack_origin_git)
self.assertTrue(git_install.called)
self.assertFalse(action_set.called)
self.assertFalse(action_fail.called)
@patch.object(git_reinstall, 'action_set')
@patch.object(git_reinstall, 'action_fail')
@patch.object(git_reinstall, 'git_install')
@patch('charmhelpers.contrib.openstack.utils.config')
def test_git_reinstall_not_configured(self, _config, git_install,
action_fail, action_set):
_config.return_value = None
git_reinstall.git_reinstall()
msg = 'openstack-origin-git is not configured'
action_fail.assert_called_with(msg)
self.assertFalse(git_install.called)
self.assertFalse(action_set.called)
@patch.object(git_reinstall, 'action_set')
@patch.object(git_reinstall, 'action_fail')
@patch.object(git_reinstall, 'git_install')
@patch('charmhelpers.contrib.openstack.utils.config')
def test_git_reinstall_exception(self, _config, git_install,
action_fail, action_set):
_config.return_value = openstack_origin_git
e = OSError('something bad happened')
git_install.side_effect = e
traceback = (
"Traceback (most recent call last):\n"
" File \"actions/git_reinstall.py\", line 37, in git_reinstall\n"
" git_install(config(\'openstack-origin-git\'))\n"
" File \"/usr/lib/python2.7/dist-packages/mock.py\", line 964, in __call__\n" # noqa
" return _mock_self._mock_call(*args, **kwargs)\n"
" File \"/usr/lib/python2.7/dist-packages/mock.py\", line 1019, in _mock_call\n" # noqa
" raise effect\n"
"OSError: something bad happened\n")
git_reinstall.git_reinstall()
msg = 'git-reinstall resulted in an unexpected error'
action_fail.assert_called_with(msg)
action_set.assert_called_with({'traceback': traceback})

View File

@ -9,9 +9,7 @@ import mock
from charmhelpers.core import hookenv
_conf = hookenv.config
hookenv.config = mock.MagicMock()
import nova_cc_utils as _utils
# this assert is a double check + to avoid pep8 warning
assert _utils.config == hookenv.config
import nova_cc_utils as _utils # noqa
hookenv.config = _conf
#####
@ -47,6 +45,8 @@ class NovaComputeContextTests(CharmTestCase):
self.config.side_effect = self.test_config.get
self.log.side_effect = fake_log
@mock.patch.object(context, 'resolve_address',
lambda *args, **kwargs: None)
@mock.patch.object(utils, 'os_release')
@mock.patch('charmhelpers.contrib.network.ip.log')
def test_instance_console_context_without_memcache(self, os_release, log_):
@ -57,6 +57,8 @@ class NovaComputeContextTests(CharmTestCase):
self.assertEqual({'memcached_servers': ''},
instance_console())
@mock.patch.object(context, 'resolve_address',
lambda *args, **kwargs: None)
@mock.patch.object(utils, 'os_release')
@mock.patch('charmhelpers.contrib.network.ip.log')
def test_instance_console_context_with_memcache(self, os_release, log_):
@ -64,6 +66,8 @@ class NovaComputeContextTests(CharmTestCase):
'127.0.1.1',
'127.0.1.1')
@mock.patch.object(context, 'resolve_address',
lambda *args, **kwargs: None)
@mock.patch.object(utils, 'os_release')
@mock.patch('charmhelpers.contrib.network.ip.log')
def test_instance_console_context_with_memcache_ipv6(self, os_release,

View File

@ -1,6 +1,7 @@
from mock import MagicMock, patch, call
from test_utils import CharmTestCase, patch_open
import os
import yaml
with patch('charmhelpers.core.hookenv.config') as config:
config.return_value = 'neutron'
@ -69,6 +70,8 @@ TO_PATCH = [
'get_iface_for_address',
'get_netmask_for_address',
'update_nrpe_config',
'git_install',
'git_install_requested',
]
@ -106,18 +109,70 @@ class NovaCCHooksTests(CharmTestCase):
self.disable_services.assert_called()
self.cmd_all_services.assert_called_with('stop')
def test_install_hook_git(self):
self.git_install_requested.return_value = True
self.determine_packages.return_value = ['foo', 'bar']
self.determine_ports.return_value = [80, 81, 82]
repo = 'cloud:trusty-juno'
openstack_origin_git = {
'repositories': [
{'name': 'requirements',
'repository': 'git://git.openstack.org/openstack/requirements', # noqa
'branch': 'stable/juno'},
{'name': 'nova',
'repository': 'git://git.openstack.org/openstack/nova',
'branch': 'stable/juno'}
],
'directory': '/mnt/openstack-git',
}
projects_yaml = yaml.dump(openstack_origin_git)
self.test_config.set('openstack-origin', repo)
self.test_config.set('openstack-origin-git', projects_yaml)
hooks.install()
self.git_install.assert_called_with(projects_yaml)
self.apt_install.assert_called_with(['foo', 'bar'], fatal=True)
self.execd_preinstall.assert_called()
self.disable_services.assert_called()
self.cmd_all_services.assert_called_with('stop')
@patch.object(hooks, 'configure_https')
def test_config_changed_no_upgrade(self, conf_https):
self.git_install_requested.return_value = False
self.openstack_upgrade_available.return_value = False
hooks.config_changed()
self.assertTrue(self.save_script_rc.called)
@patch.object(hooks, 'config_value_changed')
@patch.object(hooks, 'configure_https')
def test_config_changed_git(self, configure_https, config_val_changed):
self.git_install_requested.return_value = True
repo = 'cloud:trusty-juno'
openstack_origin_git = {
'repositories': [
{'name': 'requirements',
'repository':
'git://git.openstack.org/openstack/requirements',
'branch': 'stable/juno'},
{'name': 'nova',
'repository': 'git://git.openstack.org/openstack/nova',
'branch': 'stable/juno'}
],
'directory': '/mnt/openstack-git',
}
projects_yaml = yaml.dump(openstack_origin_git)
self.test_config.set('openstack-origin', repo)
self.test_config.set('openstack-origin-git', projects_yaml)
hooks.config_changed()
self.git_install.assert_called_with(projects_yaml)
self.assertFalse(self.do_openstack_upgrade.called)
@patch.object(hooks, 'cluster_joined')
@patch.object(hooks, 'identity_joined')
@patch.object(hooks, 'neutron_api_relation_joined')
@patch.object(hooks, 'configure_https')
def test_config_changed_with_upgrade(self, conf_https, neutron_api_joined,
identity_joined, cluster_joined):
self.git_install_requested.return_value = False
self.openstack_upgrade_available.return_value = True
self.relation_ids.return_value = ['generic_rid']
_zmq_joined = self.patch('zeromq_configuration_relation_joined')

View File

@ -127,6 +127,15 @@ DPKG_OPTS = [
'--option', 'Dpkg::Options::=--force-confdef',
]
openstack_origin_git = \
"""repositories:
- {name: requirements,
repository: 'git://git.openstack.org/openstack/requirements',
branch: stable/juno}
- {name: nova,
repository: 'git://git.openstack.org/openstack/nova',
branch: stable/juno}"""
def fake_plugin_attribute(plugin, attr, net_manager):
if plugin in PLUGIN_ATTRIBUTES:
@ -298,26 +307,34 @@ class NovaCCUtilsTests(CharmTestCase):
self.assertEquals(_proxy_page, None)
@patch('charmhelpers.contrib.openstack.context.SubordinateConfigContext')
def test_determine_packages_quantum(self, subcontext):
@patch.object(utils, 'git_install_requested')
def test_determine_packages_quantum(self, git_requested, subcontext):
git_requested.return_value = False
self._resource_map(network_manager='quantum')
pkgs = utils.determine_packages()
self.assertIn('quantum-server', pkgs)
@patch('charmhelpers.contrib.openstack.context.SubordinateConfigContext')
def test_determine_packages_neutron(self, subcontext):
@patch.object(utils, 'git_install_requested')
def test_determine_packages_neutron(self, git_requested, subcontext):
git_requested.return_value = False
self.is_relation_made.return_value = False
self._resource_map(network_manager='neutron')
pkgs = utils.determine_packages()
self.assertIn('neutron-server', pkgs)
@patch('charmhelpers.contrib.openstack.context.SubordinateConfigContext')
def test_determine_packages_nova_volume(self, subcontext):
@patch.object(utils, 'git_install_requested')
def test_determine_packages_nova_volume(self, git_requested, subcontext):
git_requested.return_value = False
self.relation_ids.return_value = ['nova-volume-service:0']
pkgs = utils.determine_packages()
self.assertIn('nova-api-os-volume', pkgs)
@patch('charmhelpers.contrib.openstack.context.SubordinateConfigContext')
def test_determine_packages_console(self, subcontext):
@patch.object(utils, 'git_install_requested')
def test_determine_packages_console(self, git_requested, subcontext):
git_requested.return_value = False
self.test_config.set('console-access-protocol', 'spice')
self.relation_ids.return_value = []
pkgs = utils.determine_packages()
@ -326,7 +343,9 @@ class NovaCCUtilsTests(CharmTestCase):
self.assertIn(console_pkg, pkgs)
@patch('charmhelpers.contrib.openstack.context.SubordinateConfigContext')
def test_determine_packages_base(self, subcontext):
@patch.object(utils, 'git_install_requested')
def test_determine_packages_base(self, git_requested, subcontext):
git_requested.return_value = False
self.relation_ids.return_value = []
self.os_release.return_value = 'folsom'
pkgs = utils.determine_packages()
@ -334,7 +353,10 @@ class NovaCCUtilsTests(CharmTestCase):
self.assertEquals(ex, pkgs)
@patch('charmhelpers.contrib.openstack.context.SubordinateConfigContext')
def test_determine_packages_base_grizzly_beyond(self, subcontext):
@patch.object(utils, 'git_install_requested')
def test_determine_packages_base_grizzly_beyond(self, git_requested,
subcontext):
git_requested.return_value = False
self.relation_ids.return_value = []
self.os_release.return_value = 'grizzly'
pkgs = utils.determine_packages()
@ -824,3 +846,237 @@ class NovaCCUtilsTests(CharmTestCase):
self.assertFalse(self.service_running.called)
self.assertFalse(self.service_stop.called)
self.assertTrue(contexts.complete_contexts.called)
@patch.object(utils, 'git_install_requested')
@patch.object(utils, 'git_clone_and_install')
@patch.object(utils, 'git_post_install')
@patch.object(utils, 'git_pre_install')
def test_git_install(self, git_pre, git_post, git_clone_and_install,
git_requested):
projects_yaml = openstack_origin_git
git_requested.return_value = True
utils.git_install(projects_yaml)
self.assertTrue(git_pre.called)
git_clone_and_install.assert_called_with(openstack_origin_git,
core_project='nova')
self.assertTrue(git_post.called)
@patch.object(utils, 'mkdir')
@patch.object(utils, 'add_user_to_group')
@patch.object(utils, 'add_group')
@patch.object(utils, 'adduser')
@patch('subprocess.check_call')
def test_git_pre_install(self, check_call, adduser, add_group,
add_user_to_group, mkdir):
utils.git_pre_install()
adduser.assert_called_with('nova', shell='/bin/bash',
system_user=True)
check_call.assert_called_with(['usermod', '--home', '/var/lib/nova',
'nova'])
add_group.assert_called_with('nova', system_group=True)
expected = [
call('nova', 'nova'),
]
self.assertEquals(add_user_to_group.call_args_list, expected)
expected = [
call('/var/lib/nova', owner='nova',
group='nova', perms=0755, force=False),
call('/var/lib/nova/buckets', owner='nova',
group='nova', perms=0755, force=False),
call('/var/lib/nova/CA', owner='nova',
group='nova', perms=0755, force=False),
call('/var/lib/nova/CA/INTER', owner='nova',
group='nova', perms=0755, force=False),
call('/var/lib/nova/CA/newcerts', owner='nova',
group='nova', perms=0755, force=False),
call('/var/lib/nova/CA/private', owner='nova',
group='nova', perms=0755, force=False),
call('/var/lib/nova/CA/reqs', owner='nova',
group='nova', perms=0755, force=False),
call('/var/lib/nova/images', owner='nova',
group='nova', perms=0755, force=False),
call('/var/lib/nova/instances', owner='nova',
group='nova', perms=0755, force=False),
call('/var/lib/nova/keys', owner='nova',
group='nova', perms=0755, force=False),
call('/var/lib/nova/networks', owner='nova',
group='nova', perms=0755, force=False),
call('/var/lib/nova/tmp', owner='nova',
group='nova', perms=0755, force=False),
call('/var/log/nova', owner='nova',
group='nova', perms=0755, force=False),
]
self.assertEquals(mkdir.call_args_list, expected)
@patch.object(utils, 'git_src_dir')
@patch.object(utils, 'render')
@patch('os.path.join')
@patch('os.path.exists')
@patch('shutil.copytree')
@patch('shutil.rmtree')
def test_git_post_install(self, rmtree, copytree, exists, join, render,
git_src_dir):
projects_yaml = openstack_origin_git
join.return_value = 'joined-string'
utils.git_post_install(projects_yaml)
expected = [
call('joined-string', '/etc/nova'),
]
copytree.assert_has_calls(expected)
nova_cc = 'nova-cloud-controller'
nova_user = 'nova'
start_dir = '/var/lib/nova'
nova_conf = '/etc/nova/nova.conf'
nova_ec2_api_context = {
'service_description': 'Nova EC2 API server',
'service_name': nova_cc,
'user_name': nova_user,
'start_dir': start_dir,
'process_name': 'nova-api-ec2',
'executable_name': '/usr/local/bin/nova-api-ec2',
'config_files': [nova_conf],
}
nova_api_os_compute_context = {
'service_description': 'Nova OpenStack Compute API server',
'service_name': nova_cc,
'user_name': nova_user,
'start_dir': start_dir,
'process_name': 'nova-api-os-compute',
'executable_name': '/usr/local/bin/nova-api-os-compute',
'config_files': [nova_conf],
}
nova_cells_context = {
'service_description': 'Nova cells',
'service_name': nova_cc,
'user_name': nova_user,
'start_dir': start_dir,
'process_name': 'nova-cells',
'executable_name': '/usr/local/bin/nova-cells',
'config_files': [nova_conf],
}
nova_cert_context = {
'service_description': 'Nova cert',
'service_name': nova_cc,
'user_name': nova_user,
'start_dir': start_dir,
'process_name': 'nova-cert',
'executable_name': '/usr/local/bin/nova-cert',
'config_files': [nova_conf],
}
nova_conductor_context = {
'service_description': 'Nova conductor',
'service_name': nova_cc,
'user_name': nova_user,
'start_dir': start_dir,
'process_name': 'nova-conductor',
'executable_name': '/usr/local/bin/nova-conductor',
'config_files': [nova_conf],
}
nova_consoleauth_context = {
'service_description': 'Nova console auth',
'service_name': nova_cc,
'user_name': nova_user,
'start_dir': start_dir,
'process_name': 'nova-consoleauth',
'executable_name': '/usr/local/bin/nova-consoleauth',
'config_files': [nova_conf],
}
nova_console_context = {
'service_description': 'Nova console',
'service_name': nova_cc,
'user_name': nova_user,
'start_dir': start_dir,
'process_name': 'nova-console',
'executable_name': '/usr/local/bin/nova-console',
'config_files': [nova_conf],
}
nova_novncproxy_context = {
'service_description': 'Nova NoVNC proxy',
'service_name': nova_cc,
'user_name': nova_user,
'start_dir': start_dir,
'process_name': 'nova-novncproxy',
'executable_name': '/usr/local/bin/nova-novncproxy',
'config_files': [nova_conf],
}
nova_objectstore_context = {
'service_description': 'Nova object store',
'service_name': nova_cc,
'user_name': nova_user,
'start_dir': start_dir,
'process_name': 'nova-objectstore',
'executable_name': '/usr/local/bin/nova-objectstore',
'config_files': [nova_conf],
}
nova_scheduler_context = {
'service_description': 'Nova scheduler',
'service_name': nova_cc,
'user_name': nova_user,
'start_dir': start_dir,
'process_name': 'nova-scheduler',
'executable_name': '/usr/local/bin/nova-scheduler',
'config_files': [nova_conf],
}
nova_spiceproxy_context = {
'service_description': 'Nova spice proxy',
'service_name': nova_cc,
'user_name': nova_user,
'start_dir': start_dir,
'process_name': 'nova-spicehtml5proxy',
'executable_name': '/usr/local/bin/nova-spicehtml5proxy',
'config_files': [nova_conf],
}
nova_xvpvncproxy_context = {
'service_description': 'Nova XVPVNC proxy',
'service_name': nova_cc,
'user_name': nova_user,
'start_dir': start_dir,
'process_name': 'nova-xvpvncproxy',
'executable_name': '/usr/local/bin/nova-xvpvncproxy',
'config_files': [nova_conf],
}
expected = [
call('git/nova_sudoers', '/etc/sudoers.d/nova_sudoers',
{}, perms=0o440),
call('git.upstart', '/etc/init/nova-api-ec2.conf',
nova_ec2_api_context, perms=0o644,
templates_dir='joined-string'),
call('git.upstart', '/etc/init/nova-api-os-compute.conf',
nova_api_os_compute_context, perms=0o644,
templates_dir='joined-string'),
call('git.upstart', '/etc/init/nova-cells.conf',
nova_cells_context, perms=0o644,
templates_dir='joined-string'),
call('git.upstart', '/etc/init/nova-cert.conf',
nova_cert_context, perms=0o644,
templates_dir='joined-string'),
call('git.upstart', '/etc/init/nova-conductor.conf',
nova_conductor_context, perms=0o644,
templates_dir='joined-string'),
call('git.upstart', '/etc/init/nova-consoleauth.conf',
nova_consoleauth_context, perms=0o644,
templates_dir='joined-string'),
call('git.upstart', '/etc/init/nova-console.conf',
nova_console_context, perms=0o644,
templates_dir='joined-string'),
call('git.upstart', '/etc/init/nova-novncproxy.conf',
nova_novncproxy_context, perms=0o644,
templates_dir='joined-string'),
call('git.upstart', '/etc/init/nova-objectstore.conf',
nova_objectstore_context, perms=0o644,
templates_dir='joined-string'),
call('git.upstart', '/etc/init/nova-scheduler.conf',
nova_scheduler_context, perms=0o644,
templates_dir='joined-string'),
call('git.upstart', '/etc/init/nova-spiceproxy.conf',
nova_spiceproxy_context, perms=0o644,
templates_dir='joined-string'),
call('git.upstart', '/etc/init/nova-xvpvncproxy.conf',
nova_xvpvncproxy_context, perms=0o644,
templates_dir='joined-string'),
]
self.assertEquals(render.call_args_list, expected)
self.assertTrue(self.apt_update.called)
self.apt_install.assert_called_with(['novnc', 'spice-html5',
'websockify'], fatal=True)