Merge lp:~james-page/charms/trusty/glance/network-splits into lp:~openstack-charmers-archive/charms/trusty/glance/next

Proposed by James Page
Status: Merged
Merged at revision: 55
Proposed branch: lp:~james-page/charms/trusty/glance/network-splits
Merge into: lp:~openstack-charmers-archive/charms/trusty/glance/next
Diff against target: 1049 lines (+511/-168)
23 files modified
.bzrignore (+2/-0)
Makefile (+11/-5)
charm-helpers-hooks.yaml (+2/-1)
config.yaml (+28/-9)
hooks/charmhelpers/contrib/hahelpers/cluster.py (+2/-2)
hooks/charmhelpers/contrib/network/ip.py (+156/-0)
hooks/charmhelpers/contrib/openstack/context.py (+28/-3)
hooks/charmhelpers/contrib/openstack/ip.py (+75/-0)
hooks/charmhelpers/contrib/openstack/templates/ceph.conf (+15/-0)
hooks/charmhelpers/contrib/openstack/templates/haproxy.cfg (+41/-0)
hooks/charmhelpers/contrib/openstack/templates/openstack_https_frontend (+23/-0)
hooks/charmhelpers/contrib/openstack/templates/openstack_https_frontend.conf (+23/-0)
hooks/charmhelpers/core/host.py (+4/-0)
hooks/glance_contexts.py (+1/-0)
hooks/glance_relations.py (+62/-28)
hooks/glance_utils.py (+1/-1)
templates/haproxy.cfg (+0/-36)
tests/charmhelpers/contrib/amulet/deployment.py (+14/-19)
tests/charmhelpers/contrib/amulet/utils.py (+2/-2)
tests/charmhelpers/contrib/openstack/amulet/deployment.py (+3/-5)
tests/charmhelpers/contrib/openstack/amulet/utils.py (+1/-45)
unit_tests/test_glance_relations.py (+14/-9)
unit_tests/test_utils.py (+3/-3)
To merge this branch: bzr merge lp:~james-page/charms/trusty/glance/network-splits
Reviewer Review Type Date Requested Status
James Page Needs Resubmitting
James Troup (community) Needs Fixing
OpenStack Charmers Pending
Review via email: mp+228141@code.launchpad.net

Description of the change

Add support for multiple network configuration.

To post a comment you must log in.
Revision history for this message
James Troup (elmo) wrote :

This has merge conflicts artifacts in the diff.

review: Needs Fixing
72. By James Page

Rebase

Revision history for this message
James Page (james-page) :
review: Needs Resubmitting

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== added file '.bzrignore'
--- .bzrignore 1970-01-01 00:00:00 +0000
+++ .bzrignore 2014-07-25 09:37:37 +0000
@@ -0,0 +1,2 @@
1.coverage
2bin
03
=== modified file 'Makefile'
--- Makefile 2014-07-18 09:45:47 +0000
+++ Makefile 2014-07-25 09:37:37 +0000
@@ -1,4 +1,5 @@
1#!/usr/bin/make1#!/usr/bin/make
2PYTHON := /usr/bin/env python
23
3lint:4lint:
4 @echo "Running flake8 tests: "5 @echo "Running flake8 tests: "
@@ -8,12 +9,17 @@
8 @charm proof9 @charm proof
9 @echo "OK"10 @echo "OK"
1011
11sync:
12 @charm-helper-sync -c charm-helpers-hooks.yaml
13 @charm-helper-sync -c charm-helpers-tests.yaml
14
15unit_test:12unit_test:
16 @$(PYTHON) /usr/bin/nosetests --nologcapture --with-coverage unit_tests13 @$(PYTHON) /usr/bin/nosetests --nologcapture --with-coverage unit_tests
14
15bin/charm_helpers_sync.py:
16 @mkdir -p bin
17 @bzr cat lp:charm-helpers/tools/charm_helpers_sync/charm_helpers_sync.py \
18 > bin/charm_helpers_sync.py
19
20sync: bin/charm_helpers_sync.py
21 @$(PYTHON) bin/charm_helpers_sync.py -c charm-helpers-hooks.yaml
22 @$(PYTHON) bin/charm_helpers_sync.py -c charm-helpers-tests.yaml
1723
18test:24test:
19 @echo Starting Amulet tests...25 @echo Starting Amulet tests...
2026
=== modified file 'charm-helpers-hooks.yaml'
--- charm-helpers-hooks.yaml 2014-06-24 19:54:45 +0000
+++ charm-helpers-hooks.yaml 2014-07-25 09:37:37 +0000
@@ -3,7 +3,8 @@
3include:3include:
4 - core4 - core
5 - fetch5 - fetch
6 - contrib.openstack6 - contrib.openstack|inc=*
7 - contrib.hahelpers7 - contrib.hahelpers
8 - contrib.storage.linux.ceph8 - contrib.storage.linux.ceph
9 - payload.execd9 - payload.execd
10 - contrib.network.ip
1011
=== modified file 'config.yaml'
--- config.yaml 2014-04-12 16:55:29 +0000
+++ config.yaml 2014-07-25 09:37:37 +0000
@@ -52,15 +52,11 @@
52 # HA configuration settings52 # HA configuration settings
53 vip:53 vip:
54 type: string54 type: string
55 description: "Virtual IP to use to front Glance API in ha configuration"55 description: |
56 vip_iface:56 Virtual IP(s) to use to front API services in HA configuration.
57 type: string57 .
58 default: eth058 If multiple networks are being used, a VIP should be provided for each
59 description: "Network Interface where to place the Virtual IP"59 network, separated by spaces.
60 vip_cidr:
61 type: int
62 default: 24
63 description: "Netmask that will be used for the Virtual IP"
64 ha-bindiface:60 ha-bindiface:
65 type: string61 type: string
66 default: eth062 default: eth0
@@ -96,4 +92,27 @@
96 default: openstack92 default: openstack
97 type: string93 type: string
98 description: RabbitMQ virtual host to request access on rabbitmq-server.94 description: RabbitMQ virtual host to request access on rabbitmq-server.
95 # Network configuration options
96 # by default all access is over 'private-address'
97 os-admin-network:
98 type: string
99 description: |
100 The IP address and netmask of the OpenStack Admin network (e.g.,
101 192.168.0.0/24)
102 .
103 This network will be used for admin endpoints.
104 os-internal-network:
105 type: string
106 description: |
107 The IP address and netmask of the OpenStack Internal network (e.g.,
108 192.168.0.0/24)
109 .
110 This network will be used for internal endpoints.
111 os-public-network:
112 type: string
113 description: |
114 The IP address and netmask of the OpenStack Public network (e.g.,
115 192.168.0.0/24)
116 .
117 This network will be used for public endpoints.
99118
100119
=== modified file 'hooks/charmhelpers/contrib/hahelpers/cluster.py'
--- hooks/charmhelpers/contrib/hahelpers/cluster.py 2014-07-10 21:43:51 +0000
+++ hooks/charmhelpers/contrib/hahelpers/cluster.py 2014-07-25 09:37:37 +0000
@@ -146,12 +146,12 @@
146 Obtains all relevant configuration from charm configuration required146 Obtains all relevant configuration from charm configuration required
147 for initiating a relation to hacluster:147 for initiating a relation to hacluster:
148148
149 ha-bindiface, ha-mcastport, vip, vip_iface, vip_cidr149 ha-bindiface, ha-mcastport, vip
150150
151 returns: dict: A dict containing settings keyed by setting name.151 returns: dict: A dict containing settings keyed by setting name.
152 raises: HAIncompleteConfig if settings are missing.152 raises: HAIncompleteConfig if settings are missing.
153 '''153 '''
154 settings = ['ha-bindiface', 'ha-mcastport', 'vip', 'vip_iface', 'vip_cidr']154 settings = ['ha-bindiface', 'ha-mcastport', 'vip']
155 conf = {}155 conf = {}
156 for setting in settings:156 for setting in settings:
157 conf[setting] = config_get(setting)157 conf[setting] = config_get(setting)
158158
=== added directory 'hooks/charmhelpers/contrib/network'
=== added file 'hooks/charmhelpers/contrib/network/__init__.py'
=== added file 'hooks/charmhelpers/contrib/network/ip.py'
--- hooks/charmhelpers/contrib/network/ip.py 1970-01-01 00:00:00 +0000
+++ hooks/charmhelpers/contrib/network/ip.py 2014-07-25 09:37:37 +0000
@@ -0,0 +1,156 @@
1import sys
2
3from functools import partial
4
5from charmhelpers.fetch import apt_install
6from charmhelpers.core.hookenv import (
7 ERROR, log,
8)
9
10try:
11 import netifaces
12except ImportError:
13 apt_install('python-netifaces')
14 import netifaces
15
16try:
17 import netaddr
18except ImportError:
19 apt_install('python-netaddr')
20 import netaddr
21
22
23def _validate_cidr(network):
24 try:
25 netaddr.IPNetwork(network)
26 except (netaddr.core.AddrFormatError, ValueError):
27 raise ValueError("Network (%s) is not in CIDR presentation format" %
28 network)
29
30
31def get_address_in_network(network, fallback=None, fatal=False):
32 """
33 Get an IPv4 or IPv6 address within the network from the host.
34
35 :param network (str): CIDR presentation format. For example,
36 '192.168.1.0/24'.
37 :param fallback (str): If no address is found, return fallback.
38 :param fatal (boolean): If no address is found, fallback is not
39 set and fatal is True then exit(1).
40
41 """
42
43 def not_found_error_out():
44 log("No IP address found in network: %s" % network,
45 level=ERROR)
46 sys.exit(1)
47
48 if network is None:
49 if fallback is not None:
50 return fallback
51 else:
52 if fatal:
53 not_found_error_out()
54
55 _validate_cidr(network)
56 network = netaddr.IPNetwork(network)
57 for iface in netifaces.interfaces():
58 addresses = netifaces.ifaddresses(iface)
59 if network.version == 4 and netifaces.AF_INET in addresses:
60 addr = addresses[netifaces.AF_INET][0]['addr']
61 netmask = addresses[netifaces.AF_INET][0]['netmask']
62 cidr = netaddr.IPNetwork("%s/%s" % (addr, netmask))
63 if cidr in network:
64 return str(cidr.ip)
65 if network.version == 6 and netifaces.AF_INET6 in addresses:
66 for addr in addresses[netifaces.AF_INET6]:
67 if not addr['addr'].startswith('fe80'):
68 cidr = netaddr.IPNetwork("%s/%s" % (addr['addr'],
69 addr['netmask']))
70 if cidr in network:
71 return str(cidr.ip)
72
73 if fallback is not None:
74 return fallback
75
76 if fatal:
77 not_found_error_out()
78
79 return None
80
81
82def is_ipv6(address):
83 '''Determine whether provided address is IPv6 or not'''
84 try:
85 address = netaddr.IPAddress(address)
86 except netaddr.AddrFormatError:
87 # probably a hostname - so not an address at all!
88 return False
89 else:
90 return address.version == 6
91
92
93def is_address_in_network(network, address):
94 """
95 Determine whether the provided address is within a network range.
96
97 :param network (str): CIDR presentation format. For example,
98 '192.168.1.0/24'.
99 :param address: An individual IPv4 or IPv6 address without a net
100 mask or subnet prefix. For example, '192.168.1.1'.
101 :returns boolean: Flag indicating whether address is in network.
102 """
103 try:
104 network = netaddr.IPNetwork(network)
105 except (netaddr.core.AddrFormatError, ValueError):
106 raise ValueError("Network (%s) is not in CIDR presentation format" %
107 network)
108 try:
109 address = netaddr.IPAddress(address)
110 except (netaddr.core.AddrFormatError, ValueError):
111 raise ValueError("Address (%s) is not in correct presentation format" %
112 address)
113 if address in network:
114 return True
115 else:
116 return False
117
118
119def _get_for_address(address, key):
120 """Retrieve an attribute of or the physical interface that
121 the IP address provided could be bound to.
122
123 :param address (str): An individual IPv4 or IPv6 address without a net
124 mask or subnet prefix. For example, '192.168.1.1'.
125 :param key: 'iface' for the physical interface name or an attribute
126 of the configured interface, for example 'netmask'.
127 :returns str: Requested attribute or None if address is not bindable.
128 """
129 address = netaddr.IPAddress(address)
130 for iface in netifaces.interfaces():
131 addresses = netifaces.ifaddresses(iface)
132 if address.version == 4 and netifaces.AF_INET in addresses:
133 addr = addresses[netifaces.AF_INET][0]['addr']
134 netmask = addresses[netifaces.AF_INET][0]['netmask']
135 cidr = netaddr.IPNetwork("%s/%s" % (addr, netmask))
136 if address in cidr:
137 if key == 'iface':
138 return iface
139 else:
140 return addresses[netifaces.AF_INET][0][key]
141 if address.version == 6 and netifaces.AF_INET6 in addresses:
142 for addr in addresses[netifaces.AF_INET6]:
143 if not addr['addr'].startswith('fe80'):
144 cidr = netaddr.IPNetwork("%s/%s" % (addr['addr'],
145 addr['netmask']))
146 if address in cidr:
147 if key == 'iface':
148 return iface
149 else:
150 return addr[key]
151 return None
152
153
154get_iface_for_address = partial(_get_for_address, key='iface')
155
156get_netmask_for_address = partial(_get_for_address, key='netmask')
0157
=== modified file 'hooks/charmhelpers/contrib/openstack/context.py'
--- hooks/charmhelpers/contrib/openstack/context.py 2014-07-10 21:43:51 +0000
+++ hooks/charmhelpers/contrib/openstack/context.py 2014-07-25 09:37:37 +0000
@@ -21,6 +21,7 @@
21 relation_get,21 relation_get,
22 relation_ids,22 relation_ids,
23 related_units,23 related_units,
24 relation_set,
24 unit_get,25 unit_get,
25 unit_private_ip,26 unit_private_ip,
26 ERROR,27 ERROR,
@@ -43,6 +44,8 @@
43 neutron_plugin_attribute,44 neutron_plugin_attribute,
44)45)
4546
47from charmhelpers.contrib.network.ip import get_address_in_network
48
46CA_CERT_PATH = '/usr/local/share/ca-certificates/keystone_juju_ca_cert.crt'49CA_CERT_PATH = '/usr/local/share/ca-certificates/keystone_juju_ca_cert.crt'
4750
4851
@@ -135,8 +138,26 @@
135 'Missing required charm config options. '138 'Missing required charm config options. '
136 '(database name and user)')139 '(database name and user)')
137 raise OSContextError140 raise OSContextError
141
138 ctxt = {}142 ctxt = {}
139143
144 # NOTE(jamespage) if mysql charm provides a network upon which
145 # access to the database should be made, reconfigure relation
146 # with the service units local address and defer execution
147 access_network = relation_get('access-network')
148 if access_network is not None:
149 if self.relation_prefix is not None:
150 hostname_key = "{}_hostname".format(self.relation_prefix)
151 else:
152 hostname_key = "hostname"
153 access_hostname = get_address_in_network(access_network,
154 unit_get('private-address'))
155 set_hostname = relation_get(attribute=hostname_key,
156 unit=local_unit())
157 if set_hostname != access_hostname:
158 relation_set(relation_settings={hostname_key: access_hostname})
159 return ctxt # Defer any further hook execution for now....
160
140 password_setting = 'password'161 password_setting = 'password'
141 if self.relation_prefix:162 if self.relation_prefix:
142 password_setting = self.relation_prefix + '_password'163 password_setting = self.relation_prefix + '_password'
@@ -341,10 +362,12 @@
341 use_syslog = str(config('use-syslog')).lower()362 use_syslog = str(config('use-syslog')).lower()
342 for rid in relation_ids('ceph'):363 for rid in relation_ids('ceph'):
343 for unit in related_units(rid):364 for unit in related_units(rid):
344 mon_hosts.append(relation_get('private-address', rid=rid,
345 unit=unit))
346 auth = relation_get('auth', rid=rid, unit=unit)365 auth = relation_get('auth', rid=rid, unit=unit)
347 key = relation_get('key', rid=rid, unit=unit)366 key = relation_get('key', rid=rid, unit=unit)
367 ceph_addr = \
368 relation_get('ceph-public-address', rid=rid, unit=unit) or \
369 relation_get('private-address', rid=rid, unit=unit)
370 mon_hosts.append(ceph_addr)
348371
349 ctxt = {372 ctxt = {
350 'mon_hosts': ' '.join(mon_hosts),373 'mon_hosts': ' '.join(mon_hosts),
@@ -378,7 +401,9 @@
378401
379 cluster_hosts = {}402 cluster_hosts = {}
380 l_unit = local_unit().replace('/', '-')403 l_unit = local_unit().replace('/', '-')
381 cluster_hosts[l_unit] = unit_get('private-address')404 cluster_hosts[l_unit] = \
405 get_address_in_network(config('os-internal-network'),
406 unit_get('private-address'))
382407
383 for rid in relation_ids('cluster'):408 for rid in relation_ids('cluster'):
384 for unit in related_units(rid):409 for unit in related_units(rid):
385410
=== added file 'hooks/charmhelpers/contrib/openstack/ip.py'
--- hooks/charmhelpers/contrib/openstack/ip.py 1970-01-01 00:00:00 +0000
+++ hooks/charmhelpers/contrib/openstack/ip.py 2014-07-25 09:37:37 +0000
@@ -0,0 +1,75 @@
1from charmhelpers.core.hookenv import (
2 config,
3 unit_get,
4)
5
6from charmhelpers.contrib.network.ip import (
7 get_address_in_network,
8 is_address_in_network,
9 is_ipv6,
10)
11
12from charmhelpers.contrib.hahelpers.cluster import is_clustered
13
14PUBLIC = 'public'
15INTERNAL = 'int'
16ADMIN = 'admin'
17
18_address_map = {
19 PUBLIC: {
20 'config': 'os-public-network',
21 'fallback': 'public-address'
22 },
23 INTERNAL: {
24 'config': 'os-internal-network',
25 'fallback': 'private-address'
26 },
27 ADMIN: {
28 'config': 'os-admin-network',
29 'fallback': 'private-address'
30 }
31}
32
33
34def canonical_url(configs, endpoint_type=PUBLIC):
35 '''
36 Returns the correct HTTP URL to this host given the state of HTTPS
37 configuration, hacluster and charm configuration.
38
39 :configs OSTemplateRenderer: A config tempating object to inspect for
40 a complete https context.
41 :endpoint_type str: The endpoint type to resolve.
42
43 :returns str: Base URL for services on the current service unit.
44 '''
45 scheme = 'http'
46 if 'https' in configs.complete_contexts():
47 scheme = 'https'
48 address = resolve_address(endpoint_type)
49 if is_ipv6(address):
50 address = "[{}]".format(address)
51 return '%s://%s' % (scheme, address)
52
53
54def resolve_address(endpoint_type=PUBLIC):
55 resolved_address = None
56 if is_clustered():
57 if config(_address_map[endpoint_type]['config']) is None:
58 # Assume vip is simple and pass back directly
59 resolved_address = config('vip')
60 else:
61 for vip in config('vip').split():
62 if is_address_in_network(
63 config(_address_map[endpoint_type]['config']),
64 vip):
65 resolved_address = vip
66 else:
67 resolved_address = get_address_in_network(
68 config(_address_map[endpoint_type]['config']),
69 unit_get(_address_map[endpoint_type]['fallback'])
70 )
71 if resolved_address is None:
72 raise ValueError('Unable to resolve a suitable IP address'
73 ' based on charm state and configuration')
74 else:
75 return resolved_address
076
=== added file 'hooks/charmhelpers/contrib/openstack/templates/ceph.conf'
--- hooks/charmhelpers/contrib/openstack/templates/ceph.conf 1970-01-01 00:00:00 +0000
+++ hooks/charmhelpers/contrib/openstack/templates/ceph.conf 2014-07-25 09:37:37 +0000
@@ -0,0 +1,15 @@
1###############################################################################
2# [ WARNING ]
3# cinder configuration file maintained by Juju
4# local changes may be overwritten.
5###############################################################################
6[global]
7{% if auth -%}
8 auth_supported = {{ auth }}
9 keyring = /etc/ceph/$cluster.$name.keyring
10 mon host = {{ mon_hosts }}
11{% endif -%}
12 log to syslog = {{ use_syslog }}
13 err to syslog = {{ use_syslog }}
14 clog to syslog = {{ use_syslog }}
15
016
=== added file 'hooks/charmhelpers/contrib/openstack/templates/haproxy.cfg'
--- hooks/charmhelpers/contrib/openstack/templates/haproxy.cfg 1970-01-01 00:00:00 +0000
+++ hooks/charmhelpers/contrib/openstack/templates/haproxy.cfg 2014-07-25 09:37:37 +0000
@@ -0,0 +1,41 @@
1global
2 log 127.0.0.1 local0
3 log 127.0.0.1 local1 notice
4 maxconn 20000
5 user haproxy
6 group haproxy
7 spread-checks 0
8
9defaults
10 log global
11 mode tcp
12 option tcplog
13 option dontlognull
14 retries 3
15 timeout queue 1000
16 timeout connect 1000
17 timeout client 30000
18 timeout server 30000
19
20listen stats :8888
21 mode http
22 stats enable
23 stats hide-version
24 stats realm Haproxy\ Statistics
25 stats uri /
26 stats auth admin:password
27
28{% if units -%}
29{% for service, ports in service_ports.iteritems() -%}
30listen {{ service }}_ipv4 0.0.0.0:{{ ports[0] }}
31 balance roundrobin
32 {% for unit, address in units.iteritems() -%}
33 server {{ unit }} {{ address }}:{{ ports[1] }} check
34 {% endfor %}
35listen {{ service }}_ipv6 :::{{ ports[0] }}
36 balance roundrobin
37 {% for unit, address in units.iteritems() -%}
38 server {{ unit }} {{ address }}:{{ ports[1] }} check
39 {% endfor %}
40{% endfor -%}
41{% endif -%}
042
=== added file 'hooks/charmhelpers/contrib/openstack/templates/openstack_https_frontend'
--- hooks/charmhelpers/contrib/openstack/templates/openstack_https_frontend 1970-01-01 00:00:00 +0000
+++ hooks/charmhelpers/contrib/openstack/templates/openstack_https_frontend 2014-07-25 09:37:37 +0000
@@ -0,0 +1,23 @@
1{% if endpoints -%}
2{% for ext, int in endpoints -%}
3Listen {{ ext }}
4NameVirtualHost *:{{ ext }}
5<VirtualHost *:{{ ext }}>
6 ServerName {{ private_address }}
7 SSLEngine on
8 SSLCertificateFile /etc/apache2/ssl/{{ namespace }}/cert
9 SSLCertificateKeyFile /etc/apache2/ssl/{{ namespace }}/key
10 ProxyPass / http://localhost:{{ int }}/
11 ProxyPassReverse / http://localhost:{{ int }}/
12 ProxyPreserveHost on
13</VirtualHost>
14<Proxy *>
15 Order deny,allow
16 Allow from all
17</Proxy>
18<Location />
19 Order allow,deny
20 Allow from all
21</Location>
22{% endfor -%}
23{% endif -%}
024
=== added file 'hooks/charmhelpers/contrib/openstack/templates/openstack_https_frontend.conf'
--- hooks/charmhelpers/contrib/openstack/templates/openstack_https_frontend.conf 1970-01-01 00:00:00 +0000
+++ hooks/charmhelpers/contrib/openstack/templates/openstack_https_frontend.conf 2014-07-25 09:37:37 +0000
@@ -0,0 +1,23 @@
1{% if endpoints -%}
2{% for ext, int in endpoints -%}
3Listen {{ ext }}
4NameVirtualHost *:{{ ext }}
5<VirtualHost *:{{ ext }}>
6 ServerName {{ private_address }}
7 SSLEngine on
8 SSLCertificateFile /etc/apache2/ssl/{{ namespace }}/cert
9 SSLCertificateKeyFile /etc/apache2/ssl/{{ namespace }}/key
10 ProxyPass / http://localhost:{{ int }}/
11 ProxyPassReverse / http://localhost:{{ int }}/
12 ProxyPreserveHost on
13</VirtualHost>
14<Proxy *>
15 Order deny,allow
16 Allow from all
17</Proxy>
18<Location />
19 Order allow,deny
20 Allow from all
21</Location>
22{% endfor -%}
23{% endif -%}
024
=== modified file 'hooks/charmhelpers/core/host.py'
--- hooks/charmhelpers/core/host.py 2014-07-10 21:43:51 +0000
+++ hooks/charmhelpers/core/host.py 2014-07-25 09:37:37 +0000
@@ -322,6 +322,10 @@
322 import apt_pkg322 import apt_pkg
323 if not pkgcache:323 if not pkgcache:
324 apt_pkg.init()324 apt_pkg.init()
325 # Force Apt to build its cache in memory. That way we avoid race
326 # conditions with other applications building the cache in the same
327 # place.
328 apt_pkg.config.set("Dir::Cache::pkgcache", "")
325 pkgcache = apt_pkg.Cache()329 pkgcache = apt_pkg.Cache()
326 pkg = pkgcache[package]330 pkg = pkgcache[package]
327 return apt_pkg.version_compare(pkg.current_ver.ver_str, revno)331 return apt_pkg.version_compare(pkg.current_ver.ver_str, revno)
328332
=== added symlink 'hooks/cluster-relation-joined'
=== target is u'glance_relations.py'
=== modified file 'hooks/glance_contexts.py'
--- hooks/glance_contexts.py 2014-04-16 08:18:06 +0000
+++ hooks/glance_contexts.py 2014-07-25 09:37:37 +0000
@@ -78,5 +78,6 @@
7878
7979
80class LoggingConfigContext(OSContextGenerator):80class LoggingConfigContext(OSContextGenerator):
81
81 def __call__(self):82 def __call__(self):
82 return {'debug': config('debug'), 'verbose': config('verbose')}83 return {'debug': config('debug'), 'verbose': config('verbose')}
8384
=== modified file 'hooks/glance_relations.py'
--- hooks/glance_relations.py 2014-04-10 15:48:30 +0000
+++ hooks/glance_relations.py 2014-07-25 09:37:37 +0000
@@ -44,7 +44,9 @@
44)44)
4545
46from charmhelpers.contrib.hahelpers.cluster import (46from charmhelpers.contrib.hahelpers.cluster import (
47 canonical_url, eligible_leader)47 eligible_leader,
48 get_hacluster_config
49)
4850
49from charmhelpers.contrib.openstack.utils import (51from charmhelpers.contrib.openstack.utils import (
50 configure_installation_source,52 configure_installation_source,
@@ -54,6 +56,15 @@
5456
55from charmhelpers.contrib.storage.linux.ceph import ensure_ceph_keyring57from charmhelpers.contrib.storage.linux.ceph import ensure_ceph_keyring
56from charmhelpers.payload.execd import execd_preinstall58from charmhelpers.payload.execd import execd_preinstall
59from charmhelpers.contrib.network.ip import (
60 get_address_in_network,
61 get_netmask_for_address,
62 get_iface_for_address,
63)
64from charmhelpers.contrib.openstack.ip import (
65 canonical_url,
66 PUBLIC, INTERNAL, ADMIN
67)
5768
58from subprocess import (69from subprocess import (
59 check_call,70 check_call,
@@ -70,7 +81,7 @@
70 execd_preinstall()81 execd_preinstall()
71 src = config('openstack-origin')82 src = config('openstack-origin')
72 if (lsb_release()['DISTRIB_CODENAME'] == 'precise' and83 if (lsb_release()['DISTRIB_CODENAME'] == 'precise' and
73 src == 'distro'):84 src == 'distro'):
74 src = 'cloud:precise-folsom'85 src = 'cloud:precise-folsom'
7586
76 configure_installation_source(src)87 configure_installation_source(src)
@@ -163,7 +174,8 @@
163 return174 return
164175
165 relation_data = {176 relation_data = {
166 'glance-api-server': canonical_url(CONFIGS) + ":9292"177 'glance-api-server':
178 "{}:9292".format(canonical_url(CONFIGS, INTERNAL))
167 }179 }
168180
169 juju_log("%s: image-service_joined: To peer glance-api-server=%s" %181 juju_log("%s: image-service_joined: To peer glance-api-server=%s" %
@@ -222,13 +234,15 @@
222 juju_log('Deferring keystone_joined() to service leader.')234 juju_log('Deferring keystone_joined() to service leader.')
223 return235 return
224236
225 url = canonical_url(CONFIGS) + ":9292"237 public_url = '{}:9292'.format(canonical_url(CONFIGS, PUBLIC))
238 internal_url = '{}:9292'.format(canonical_url(CONFIGS, INTERNAL))
239 admin_url = '{}:9292'.format(canonical_url(CONFIGS, ADMIN))
226 relation_data = {240 relation_data = {
227 'service': 'glance',241 'service': 'glance',
228 'region': config('region'),242 'region': config('region'),
229 'public_url': url,243 'public_url': public_url,
230 'admin_url': url,244 'admin_url': admin_url,
231 'internal_url': url, }245 'internal_url': internal_url, }
232246
233 relation_set(relation_id=relation_id, **relation_data)247 relation_set(relation_id=relation_id, **relation_data)
234248
@@ -265,10 +279,19 @@
265 open_port(9292)279 open_port(9292)
266 configure_https()280 configure_https()
267281
268 # env_vars = {'OPENSTACK_PORT_MCASTPORT': config("ha-mcastport"),282 # Pickup and changes due to network reference architecture
269 # 'OPENSTACK_SERVICE_API': "glance-api",283 # configuration
270 # 'OPENSTACK_SERVICE_REGISTRY': "glance-registry"}284 [keystone_joined(rid) for rid in relation_ids('identity-service')]
271 # save_script_rc(**env_vars)285 [image_service_joined(rid) for rid in relation_ids('image-service')]
286 [cluster_joined(rid) for rid in relation_ids('cluster')]
287
288
289@hooks.hook('cluster-relation-joined')
290def cluster_joined(relation_id=None):
291 address = get_address_in_network(config('os-internal-network'),
292 unit_get('private-address'))
293 relation_set(relation_id=relation_id,
294 relation_settings={'private-address': address})
272295
273296
274@hooks.hook('cluster-relation-changed')297@hooks.hook('cluster-relation-changed')
@@ -289,33 +312,44 @@
289312
290@hooks.hook('ha-relation-joined')313@hooks.hook('ha-relation-joined')
291def ha_relation_joined():314def ha_relation_joined():
292 corosync_bindiface = config("ha-bindiface")315 config = get_hacluster_config()
293 corosync_mcastport = config("ha-mcastport")
294 vip = config("vip")
295 vip_iface = config("vip_iface")
296 vip_cidr = config("vip_cidr")
297
298 # if vip and vip_iface and vip_cidr and \
299 # corosync_bindiface and corosync_mcastport:
300316
301 resources = {317 resources = {
302 'res_glance_vip': 'ocf:heartbeat:IPaddr2',318 'res_glance_haproxy': 'lsb:haproxy'
303 'res_glance_haproxy': 'lsb:haproxy', }319 }
304320
305 resource_params = {321 resource_params = {
306 'res_glance_vip': 'params ip="%s" cidr_netmask="%s" nic="%s"' %322 'res_glance_haproxy': 'op monitor interval="5s"'
307 (vip, vip_cidr, vip_iface),323 }
308 'res_glance_haproxy': 'op monitor interval="5s"', }324
325 vip_group = []
326 for vip in config['vip'].split():
327 iface = get_iface_for_address(vip)
328 if iface is not None:
329 vip_key = 'res_glance_{}_vip'.format(iface)
330 resources[vip_key] = 'ocf:heartbeat:IPaddr2'
331 resource_params[vip_key] = (
332 'params ip="{vip}" cidr_netmask="{netmask}"'
333 ' nic="{iface}"'.format(vip=vip,
334 iface=iface,
335 netmask=get_netmask_for_address(vip))
336 )
337 vip_group.append(vip_key)
338
339 if len(vip_group) > 1:
340 relation_set(groups={'grp_glance_vips': ' '.join(vip_group)})
309341
310 init_services = {342 init_services = {
311 'res_glance_haproxy': 'haproxy', }343 'res_glance_haproxy': 'haproxy',
344 }
312345
313 clones = {346 clones = {
314 'cl_glance_haproxy': 'res_glance_haproxy', }347 'cl_glance_haproxy': 'res_glance_haproxy',
348 }
315349
316 relation_set(init_services=init_services,350 relation_set(init_services=init_services,
317 corosync_bindiface=corosync_bindiface,351 corosync_bindiface=config['ha-bindiface'],
318 corosync_mcastport=corosync_mcastport,352 corosync_mcastport=config['ha-mcastport'],
319 resources=resources,353 resources=resources,
320 resource_params=resource_params,354 resource_params=resource_params,
321 clones=clones)355 clones=clones)
322356
=== modified file 'hooks/glance_utils.py'
--- hooks/glance_utils.py 2014-04-12 17:10:59 +0000
+++ hooks/glance_utils.py 2014-07-25 09:37:37 +0000
@@ -42,7 +42,7 @@
42 get_os_codename_package,42 get_os_codename_package,
43 configure_installation_source)43 configure_installation_source)
4444
45CLUSTER_RES = "res_glance_vip"45CLUSTER_RES = "grp_glance_vips"
4646
47PACKAGES = [47PACKAGES = [
48 "apache2", "glance", "python-mysqldb", "python-swift",48 "apache2", "glance", "python-mysqldb", "python-swift",
4949
=== removed file 'templates/haproxy.cfg'
--- templates/haproxy.cfg 2014-02-16 20:40:22 +0000
+++ templates/haproxy.cfg 1970-01-01 00:00:00 +0000
@@ -1,36 +0,0 @@
1global
2 log 127.0.0.1 local0
3 log 127.0.0.1 local1 notice
4 maxconn 20000
5 user haproxy
6 group haproxy
7 spread-checks 0
8
9defaults
10 log global
11 mode tcp
12 option tcplog
13 option dontlognull
14 retries 3
15 timeout queue 1000
16 timeout connect 1000
17 timeout client 30000
18 timeout server 30000
19
20listen stats :8888
21 mode http
22 stats enable
23 stats hide-version
24 stats realm Haproxy\ Statistics
25 stats uri /
26 stats auth admin:password
27
28{% if units %}
29{% for service, ports in service_ports.iteritems() -%}
30listen {{ service }} 0.0.0.0:{{ ports[0] }}
31 balance roundrobin
32 {% for unit, address in units.iteritems() -%}
33 server {{ unit }} {{ address }}:{{ ports[1] }} check
34 {% endfor %}
35{% endfor %}
36{% endif %}
370
=== modified file 'tests/charmhelpers/contrib/amulet/deployment.py'
--- tests/charmhelpers/contrib/amulet/deployment.py 2014-07-10 21:43:51 +0000
+++ tests/charmhelpers/contrib/amulet/deployment.py 2014-07-25 09:37:37 +0000
@@ -1,40 +1,35 @@
1import amulet1import amulet
2import re
32
43
5class AmuletDeployment(object):4class AmuletDeployment(object):
6 """This class provides generic Amulet deployment and test runner5 """This class provides generic Amulet deployment and test runner
7 methods."""6 methods."""
87
9 def __init__(self, series):8 def __init__(self, series=None):
10 """Initialize the deployment environment."""9 """Initialize the deployment environment."""
11 self.series = series10 self.series = None
12 self.d = amulet.Deployment(series=self.series)
1311
14 def _get_charm_name(self, service_name):12 if series:
15 """Gets the charm name from the service name. Unique service names can13 self.series = series
16 be specified with a '-service#' suffix (e.g. mysql-service1)."""14 self.d = amulet.Deployment(series=self.series)
17 if re.match(r"^.*-service\d{1,3}$", service_name):
18 charm_name = re.sub('\-service\d{1,3}$', '', service_name)
19 else:15 else:
20 charm_name = service_name16 self.d = amulet.Deployment()
21 return charm_name
2217
23 def _add_services(self, this_service, other_services):18 def _add_services(self, this_service, other_services):
24 """Add services to the deployment where this_service is the local charm19 """Add services to the deployment where this_service is the local charm
25 that we're focused on testing and other_services are the other20 that we're focused on testing and other_services are the other
26 charms that come from the charm store."""21 charms that come from the charm store."""
27 name, units = range(2)22 name, units = range(2)
2823 self.this_service = this_service[name]
29 charm_name = self._get_charm_name(this_service[name])24 self.d.add(this_service[name], units=this_service[units])
30 self.d.add(this_service[name],
31 units=this_service[units])
3225
33 for svc in other_services:26 for svc in other_services:
34 charm_name = self._get_charm_name(svc[name])27 if self.series:
35 self.d.add(svc[name],28 self.d.add(svc[name],
36 charm='cs:{}/{}'.format(self.series, charm_name),29 charm='cs:{}/{}'.format(self.series, svc[name]),
37 units=svc[units])30 units=svc[units])
31 else:
32 self.d.add(svc[name], units=svc[units])
3833
39 def _add_relations(self, relations):34 def _add_relations(self, relations):
40 """Add all of the relations for the services."""35 """Add all of the relations for the services."""
4136
=== modified file 'tests/charmhelpers/contrib/amulet/utils.py'
--- tests/charmhelpers/contrib/amulet/utils.py 2014-07-10 21:43:51 +0000
+++ tests/charmhelpers/contrib/amulet/utils.py 2014-07-25 09:37:37 +0000
@@ -139,11 +139,11 @@
139 return self._get_dir_mtime(sentry_unit, proc_dir)139 return self._get_dir_mtime(sentry_unit, proc_dir)
140140
141 def service_restarted(self, sentry_unit, service, filename,141 def service_restarted(self, sentry_unit, service, filename,
142 pgrep_full=False, sleep_time=20):142 pgrep_full=False):
143 """Compare a service's start time vs a file's last modification time143 """Compare a service's start time vs a file's last modification time
144 (such as a config file for that service) to determine if the service144 (such as a config file for that service) to determine if the service
145 has been restarted."""145 has been restarted."""
146 sleep(sleep_time)146 sleep(10)
147 if self._get_proc_start_time(sentry_unit, service, pgrep_full) >= \147 if self._get_proc_start_time(sentry_unit, service, pgrep_full) >= \
148 self._get_file_mtime(sentry_unit, filename):148 self._get_file_mtime(sentry_unit, filename):
149 return True149 return True
150150
=== modified file 'tests/charmhelpers/contrib/openstack/amulet/deployment.py'
--- tests/charmhelpers/contrib/openstack/amulet/deployment.py 2014-07-10 21:43:51 +0000
+++ tests/charmhelpers/contrib/openstack/amulet/deployment.py 2014-07-25 09:37:37 +0000
@@ -7,7 +7,7 @@
7 """This class inherits from AmuletDeployment and has additional support7 """This class inherits from AmuletDeployment and has additional support
8 that is specifically for use by OpenStack charms."""8 that is specifically for use by OpenStack charms."""
99
10 def __init__(self, series, openstack=None, source=None):10 def __init__(self, series=None, openstack=None, source=None):
11 """Initialize the deployment environment."""11 """Initialize the deployment environment."""
12 super(OpenStackAmuletDeployment, self).__init__(series)12 super(OpenStackAmuletDeployment, self).__init__(series)
13 self.openstack = openstack13 self.openstack = openstack
@@ -24,15 +24,13 @@
2424
25 if self.openstack:25 if self.openstack:
26 for svc in services:26 for svc in services:
27 charm_name = self._get_charm_name(svc[name])27 if svc[name] not in use_source:
28 if charm_name not in use_source:
29 config = {'openstack-origin': self.openstack}28 config = {'openstack-origin': self.openstack}
30 self.d.configure(svc[name], config)29 self.d.configure(svc[name], config)
3130
32 if self.source:31 if self.source:
33 for svc in services:32 for svc in services:
34 charm_name = self._get_charm_name(svc[name])33 if svc[name] in use_source:
35 if charm_name in use_source:
36 config = {'source': self.source}34 config = {'source': self.source}
37 self.d.configure(svc[name], config)35 self.d.configure(svc[name], config)
3836
3937
=== modified file 'tests/charmhelpers/contrib/openstack/amulet/utils.py'
--- tests/charmhelpers/contrib/openstack/amulet/utils.py 2014-07-10 21:43:51 +0000
+++ tests/charmhelpers/contrib/openstack/amulet/utils.py 2014-07-25 09:37:37 +0000
@@ -177,40 +177,12 @@
177 image = glance.images.create(name=image_name, is_public=True,177 image = glance.images.create(name=image_name, is_public=True,
178 disk_format='qcow2',178 disk_format='qcow2',
179 container_format='bare', data=f)179 container_format='bare', data=f)
180 count = 1
181 status = image.status
182 while status != 'active' and count < 10:
183 time.sleep(3)
184 image = glance.images.get(image.id)
185 status = image.status
186 self.log.debug('image status: {}'.format(status))
187 count += 1
188
189 if status != 'active':
190 self.log.error('image creation timed out')
191 return None
192
193 return image180 return image
194181
195 def delete_image(self, glance, image):182 def delete_image(self, glance, image):
196 """Delete the specified image."""183 """Delete the specified image."""
197 num_before = len(list(glance.images.list()))
198 glance.images.delete(image)184 glance.images.delete(image)
199185
200 count = 1
201 num_after = len(list(glance.images.list()))
202 while num_after != (num_before - 1) and count < 10:
203 time.sleep(3)
204 num_after = len(list(glance.images.list()))
205 self.log.debug('number of images: {}'.format(num_after))
206 count += 1
207
208 if num_after != (num_before - 1):
209 self.log.error('image deletion timed out')
210 return False
211
212 return True
213
214 def create_instance(self, nova, image_name, instance_name, flavor):186 def create_instance(self, nova, image_name, instance_name, flavor):
215 """Create the specified instance."""187 """Create the specified instance."""
216 image = nova.images.find(name=image_name)188 image = nova.images.find(name=image_name)
@@ -227,27 +199,11 @@
227 self.log.debug('instance status: {}'.format(status))199 self.log.debug('instance status: {}'.format(status))
228 count += 1200 count += 1
229201
230 if status != 'ACTIVE':202 if status == 'BUILD':
231 self.log.error('instance creation timed out')
232 return None203 return None
233204
234 return instance205 return instance
235206
236 def delete_instance(self, nova, instance):207 def delete_instance(self, nova, instance):
237 """Delete the specified instance."""208 """Delete the specified instance."""
238 num_before = len(list(nova.servers.list()))
239 nova.servers.delete(instance)209 nova.servers.delete(instance)
240
241 count = 1
242 num_after = len(list(nova.servers.list()))
243 while num_after != (num_before - 1) and count < 10:
244 time.sleep(3)
245 num_after = len(list(nova.servers.list()))
246 self.log.debug('number of instances: {}'.format(num_after))
247 count += 1
248
249 if num_after != (num_before - 1):
250 self.log.error('instance deletion timed out')
251 return False
252
253 return True
254210
=== modified file 'unit_tests/test_glance_relations.py'
--- unit_tests/test_glance_relations.py 2014-03-31 11:38:11 +0000
+++ unit_tests/test_glance_relations.py 2014-07-25 09:37:37 +0000
@@ -54,7 +54,10 @@
54 'check_call',54 'check_call',
55 'execd_preinstall',55 'execd_preinstall',
56 'lsb_release',56 'lsb_release',
57 'filter_installed_packages'57 'filter_installed_packages',
58 'get_hacluster_config',
59 'get_netmask_for_address',
60 'get_iface_for_address'
58]61]
5962
6063
@@ -435,21 +438,23 @@
435 self.assertTrue(configs.write_all.called)438 self.assertTrue(configs.write_all.called)
436439
437 def test_ha_relation_joined(self):440 def test_ha_relation_joined(self):
438 self.test_config.set('ha-bindiface', 'em0')441 self.get_hacluster_config.return_value = {
439 self.test_config.set('ha-mcastport', '8080')442 'ha-bindiface': 'em0',
440 self.test_config.set('vip', '10.10.10.10')443 'ha-mcastport': '8080',
441 self.test_config.set('vip_iface', 'em1')444 'vip': '10.10.10.10',
442 self.test_config.set('vip_cidr', '24')445 }
446 self.get_iface_for_address.return_value = 'eth1'
447 self.get_netmask_for_address.return_value = '255.255.0.0'
443 relations.ha_relation_joined()448 relations.ha_relation_joined()
444 args = {449 args = {
445 'corosync_bindiface': 'em0',450 'corosync_bindiface': 'em0',
446 'corosync_mcastport': '8080',451 'corosync_mcastport': '8080',
447 'init_services': {'res_glance_haproxy': 'haproxy'},452 'init_services': {'res_glance_haproxy': 'haproxy'},
448 'resources': {'res_glance_vip': 'ocf:heartbeat:IPaddr2',453 'resources': {'res_glance_eth1_vip': 'ocf:heartbeat:IPaddr2',
449 'res_glance_haproxy': 'lsb:haproxy'},454 'res_glance_haproxy': 'lsb:haproxy'},
450 'resource_params': {455 'resource_params': {
451 'res_glance_vip': 'params ip="10.10.10.10"'456 'res_glance_eth1_vip': 'params ip="10.10.10.10"'
452 ' cidr_netmask="24" nic="em1"',457 ' cidr_netmask="255.255.0.0" nic="eth1"',
453 'res_glance_haproxy': 'op monitor interval="5s"'},458 'res_glance_haproxy': 'op monitor interval="5s"'},
454 'clones': {'cl_glance_haproxy': 'res_glance_haproxy'}459 'clones': {'cl_glance_haproxy': 'res_glance_haproxy'}
455 }460 }
456461
=== modified file 'unit_tests/test_utils.py'
--- unit_tests/test_utils.py 2014-01-15 13:04:21 +0000
+++ unit_tests/test_utils.py 2014-07-25 09:37:37 +0000
@@ -80,9 +80,9 @@
80 return self.config80 return self.config
8181
82 def set(self, attr, value):82 def set(self, attr, value):
83 if attr not in self.config:83 if attr not in self.config:
84 raise KeyError84 raise KeyError
85 self.config[attr] = value85 self.config[attr] = value
8686
8787
88class TestRelation(object):88class TestRelation(object):

Subscribers

People subscribed via source and target branches