diff --git a/charm-helpers-hooks.yaml b/charm-helpers-hooks.yaml index 779e2142..dbc1bac2 100644 --- a/charm-helpers-hooks.yaml +++ b/charm-helpers-hooks.yaml @@ -1,4 +1,4 @@ -branch: lp:charm-helpers +branch: lp:~james-page/charm-helpers/multiple-https-networks destination: hooks/charmhelpers include: - core diff --git a/hooks/charmhelpers/contrib/openstack/amulet/deployment.py b/hooks/charmhelpers/contrib/openstack/amulet/deployment.py index 10d3b506..9179eeb1 100644 --- a/hooks/charmhelpers/contrib/openstack/amulet/deployment.py +++ b/hooks/charmhelpers/contrib/openstack/amulet/deployment.py @@ -1,6 +1,3 @@ -from bzrlib.branch import Branch -import os -import re from charmhelpers.contrib.amulet.deployment import ( AmuletDeployment ) @@ -19,41 +16,11 @@ class OpenStackAmuletDeployment(AmuletDeployment): self.openstack = openstack self.source = source - def _is_dev_branch(self): - """Determine if branch being tested is a dev (i.e. next) branch.""" - branch = Branch.open(os.getcwd()) - parent = branch.get_parent() - pattern = re.compile("^.*/next/$") - if (pattern.match(parent)): - return True - else: - return False - - def _determine_branch_locations(self, other_services): - """Determine the branch locations for the other services. - - If the branch being tested is a dev branch, then determine the - development branch locations for the other services. Otherwise, - the default charm store branches will be used.""" - name = 0 - if self._is_dev_branch(): - updated_services = [] - for svc in other_services: - if svc[name] in ['mysql', 'mongodb', 'rabbitmq-server']: - location = 'lp:charms/{}'.format(svc[name]) - else: - temp = 'lp:~openstack-charmers/charms/trusty/{}/next' - location = temp.format(svc[name]) - updated_services.append(svc + (location,)) - other_services = updated_services - return other_services - def _add_services(self, this_service, other_services): - """Add services to the deployment and set openstack-origin/source.""" - name = 0 - other_services = self._determine_branch_locations(other_services) + """Add services to the deployment and set openstack-origin.""" super(OpenStackAmuletDeployment, self)._add_services(this_service, other_services) + name = 0 services = other_services services.append(this_service) use_source = ['mysql', 'mongodb', 'rabbitmq-server', 'ceph'] diff --git a/hooks/charmhelpers/contrib/openstack/amulet/utils.py b/hooks/charmhelpers/contrib/openstack/amulet/utils.py index 0f312b99..bd327bdc 100644 --- a/hooks/charmhelpers/contrib/openstack/amulet/utils.py +++ b/hooks/charmhelpers/contrib/openstack/amulet/utils.py @@ -187,16 +187,15 @@ class OpenStackAmuletUtils(AmuletUtils): f = opener.open("http://download.cirros-cloud.net/version/released") version = f.read().strip() - cirros_img = "cirros-{}-x86_64-disk.img".format(version) - local_path = os.path.join('tests', cirros_img) + cirros_img = "tests/cirros-{}-x86_64-disk.img".format(version) - if not os.path.exists(local_path): + if not os.path.exists(cirros_img): cirros_url = "http://{}/{}/{}".format("download.cirros-cloud.net", version, cirros_img) - opener.retrieve(cirros_url, local_path) + opener.retrieve(cirros_url, cirros_img) f.close() - with open(local_path) as f: + with open(cirros_img) as f: image = glance.images.create(name=image_name, is_public=True, disk_format='qcow2', container_format='bare', data=f) diff --git a/hooks/charmhelpers/contrib/openstack/context.py b/hooks/charmhelpers/contrib/openstack/context.py index 43529d5c..3af65252 100644 --- a/hooks/charmhelpers/contrib/openstack/context.py +++ b/hooks/charmhelpers/contrib/openstack/context.py @@ -52,7 +52,8 @@ from charmhelpers.contrib.openstack.neutron import ( from charmhelpers.contrib.network.ip import ( get_address_in_network, get_ipv6_addr, - is_address_in_network + is_address_in_network, + get_netmask_for_address ) CA_CERT_PATH = '/usr/local/share/ca-certificates/keystone_juju_ca_cert.crt' @@ -396,6 +397,9 @@ class CephContext(OSContextGenerator): return ctxt +ADDRESS_TYPES = ['admin', 'internal', 'public'] + + class HAProxyContext(OSContextGenerator): interfaces = ['cluster'] @@ -408,30 +412,57 @@ class HAProxyContext(OSContextGenerator): if not relation_ids('cluster'): return {} - cluster_hosts = {} l_unit = local_unit().replace('/', '-') if config('prefer-ipv6'): addr = get_ipv6_addr() else: addr = unit_get('private-address') - cluster_hosts[l_unit] = get_address_in_network(config('os-internal-network'), - addr) - for rid in relation_ids('cluster'): - for unit in related_units(rid): - _unit = unit.replace('/', '-') - addr = relation_get('private-address', rid=rid, unit=unit) - cluster_hosts[_unit] = addr + cluster_hosts = {} + + # NOTE(jamespage): build out map of configured network endpoints + # and associated backends + for addr_type in ADDRESS_TYPES: + laddr = get_address_in_network( + config('os-{}-network'.format(addr_type))) + if laddr: + cluster_hosts[laddr] = {} + cluster_hosts[laddr]['network'] = "{}/{}".format( + laddr, + get_netmask_for_address(laddr) + ) + cluster_hosts[laddr]['backends'] = {} + cluster_hosts[laddr]['backends'][l_unit] = laddr + for rid in relation_ids('cluster'): + for unit in related_units(rid): + _unit = unit.replace('/', '-') + _laddr = relation_get('{}-address'.format(addr_type), + rid=rid, unit=unit) + if _laddr: + cluster_hosts[laddr]['backends'][_unit] = _laddr + + # NOTE(jamespage) no split configurations found, just use + # private addresses + if len(cluster_hosts) < 1: + cluster_hosts[addr] = {} + cluster_hosts[addr]['network'] = "{}/{}".format( + addr, + get_netmask_for_address(addr) + ) + cluster_hosts[addr]['backends'] = {} + cluster_hosts[addr]['backends'][l_unit] = addr + for rid in relation_ids('cluster'): + for unit in related_units(rid): + _unit = unit.replace('/', '-') + _laddr = relation_get('private-address', + rid=rid, unit=unit) + if _laddr: + cluster_hosts[addr]['backends'][_unit] = _laddr ctxt = { - 'units': cluster_hosts, + 'frontends': cluster_hosts, } - if config('haproxy-server-timeout'): - ctxt['haproxy_server_timeout'] = config('haproxy-server-timeout') - if config('haproxy-client-timeout'): - ctxt['haproxy_client_timeout'] = config('haproxy-client-timeout') - if config('prefer-ipv6'): ctxt['local_host'] = 'ip6-localhost' ctxt['haproxy_host'] = '::' @@ -441,12 +472,13 @@ class HAProxyContext(OSContextGenerator): ctxt['haproxy_host'] = '0.0.0.0' ctxt['stat_port'] = ':8888' - if len(cluster_hosts.keys()) > 1: - # Enable haproxy when we have enough peers. - log('Ensuring haproxy enabled in /etc/default/haproxy.') - with open('/etc/default/haproxy', 'w') as out: - out.write('ENABLED=1\n') - return ctxt + for frontend in cluster_hosts: + if len(cluster_hosts[frontend]['backends']) > 1: + # Enable haproxy when we have enough peers. + log('Ensuring haproxy enabled in /etc/default/haproxy.') + with open('/etc/default/haproxy', 'w') as out: + out.write('ENABLED=1\n') + return ctxt log('HAProxy context is incomplete, this unit has no peers.') return {} @@ -708,22 +740,22 @@ class NeutronContext(OSContextGenerator): class OSConfigFlagContext(OSContextGenerator): - """ - Responsible for adding user-defined config-flags in charm config to a - template context. + """ + Responsible for adding user-defined config-flags in charm config to a + template context. - NOTE: the value of config-flags may be a comma-separated list of - key=value pairs and some Openstack config files support - comma-separated lists as values. - """ + NOTE: the value of config-flags may be a comma-separated list of + key=value pairs and some Openstack config files support + comma-separated lists as values. + """ - def __call__(self): - config_flags = config('config-flags') - if not config_flags: - return {} + def __call__(self): + config_flags = config('config-flags') + if not config_flags: + return {} - flags = config_flags_parser(config_flags) - return {'user_config_flags': flags} + flags = config_flags_parser(config_flags) + return {'user_config_flags': flags} class SubordinateConfigContext(OSContextGenerator): diff --git a/hooks/charmhelpers/contrib/openstack/templates/haproxy.cfg b/hooks/charmhelpers/contrib/openstack/templates/haproxy.cfg index 54c2d976..f6bfb65b 100644 --- a/hooks/charmhelpers/contrib/openstack/templates/haproxy.cfg +++ b/hooks/charmhelpers/contrib/openstack/templates/haproxy.cfg @@ -14,17 +14,8 @@ defaults retries 3 timeout queue 1000 timeout connect 1000 -{% if haproxy_client_timeout -%} - timeout client {{ haproxy_client_timeout }} -{% else -%} timeout client 30000 -{% endif -%} - -{% if haproxy_server_timeout -%} - timeout server {{ haproxy_server_timeout }} -{% else -%} timeout server 30000 -{% endif -%} listen stats {{ stat_port }} mode http @@ -34,17 +25,21 @@ listen stats {{ stat_port }} stats uri / stats auth admin:password -{% if units -%} +{% if frontends -%} {% for service, ports in service_ports.iteritems() -%} -listen {{ service }}_ipv4 0.0.0.0:{{ ports[0] }} - balance roundrobin - {% for unit, address in units.iteritems() -%} - server {{ unit }} {{ address }}:{{ ports[1] }} check +frontend tcp-in_{{ service }} + bind *:{{ ports[0] }} + bind :::{{ ports[0] }} + {% for frontend in frontends -%} + acl net_{{ frontend }} dst {{ frontends[frontend]['network'] }} + use_backend {{ service }}_{{ frontend }} if net_{{ frontend }} {% endfor %} -listen {{ service }}_ipv6 :::{{ ports[0] }} - balance roundrobin - {% for unit, address in units.iteritems() -%} +{% for frontend in frontends -%} +backend {{ service }}_{{ frontend }} + balance leastconn + {% for unit, address in frontends[frontend]['backends'].iteritems() -%} server {{ unit }} {{ address }}:{{ ports[1] }} check {% endfor %} {% endfor -%} +{% endfor -%} {% endif -%} diff --git a/tests/charmhelpers/contrib/amulet/deployment.py b/tests/charmhelpers/contrib/amulet/deployment.py index e0e850dd..d859d367 100644 --- a/tests/charmhelpers/contrib/amulet/deployment.py +++ b/tests/charmhelpers/contrib/amulet/deployment.py @@ -25,25 +25,30 @@ class AmuletDeployment(object): Add services to the deployment where this_service is the local charm that we're testing and other_services are the other services that - are being used in the amulet tests. + are being used in the local amulet tests. """ - name, units, location = range(3) - - if this_service[name] != os.path.basename(os.getcwd()): - s = this_service[name] + if this_service['name'] != os.path.basename(os.getcwd()): + s = this_service['name'] msg = "The charm's root directory name needs to be {}".format(s) amulet.raise_status(amulet.FAIL, msg=msg) - self.d.add(this_service[name], units=this_service[units]) + if 'units' not in this_service: + this_service['units'] = 1 + + self.d.add(this_service['name'], units=this_service['units']) for svc in other_services: - if len(svc) > 2: - branch_location = svc[location] + if 'location' in svc: + branch_location = svc['location'] elif self.series: - branch_location = 'cs:{}/{}'.format(self.series, svc[name]), + branch_location = 'cs:{}/{}'.format(self.series, svc['name']), else: branch_location = None - self.d.add(svc[name], charm=branch_location, units=svc[units]) + + if 'units' not in svc: + svc['units'] = 1 + + self.d.add(svc['name'], charm=branch_location, units=svc['units']) def _add_relations(self, relations): """Add all of the relations for the services.""" diff --git a/tests/charmhelpers/contrib/openstack/amulet/deployment.py b/tests/charmhelpers/contrib/openstack/amulet/deployment.py index 10d3b506..495ebdb6 100644 --- a/tests/charmhelpers/contrib/openstack/amulet/deployment.py +++ b/tests/charmhelpers/contrib/openstack/amulet/deployment.py @@ -1,6 +1,3 @@ -from bzrlib.branch import Branch -import os -import re from charmhelpers.contrib.amulet.deployment import ( AmuletDeployment ) @@ -13,62 +10,60 @@ class OpenStackAmuletDeployment(AmuletDeployment): that is specifically for use by OpenStack charms. """ - def __init__(self, series=None, openstack=None, source=None): + def __init__(self, series=None, openstack=None, source=None, stable=True): """Initialize the deployment environment.""" super(OpenStackAmuletDeployment, self).__init__(series) self.openstack = openstack self.source = source - - def _is_dev_branch(self): - """Determine if branch being tested is a dev (i.e. next) branch.""" - branch = Branch.open(os.getcwd()) - parent = branch.get_parent() - pattern = re.compile("^.*/next/$") - if (pattern.match(parent)): - return True - else: - return False + self.stable = stable + # Note(coreycb): this needs to be changed when new next branches come out. + self.current_next = "trusty" def _determine_branch_locations(self, other_services): """Determine the branch locations for the other services. - If the branch being tested is a dev branch, then determine the - development branch locations for the other services. Otherwise, - the default charm store branches will be used.""" - name = 0 - if self._is_dev_branch(): - updated_services = [] + Determine if the local branch being tested is derived from its + stable or next (dev) branch, and based on this, use the corresonding + stable or next branches for the other_services.""" + base_charms = ['mysql', 'mongodb', 'rabbitmq-server'] + + if self.stable: for svc in other_services: - if svc[name] in ['mysql', 'mongodb', 'rabbitmq-server']: - location = 'lp:charms/{}'.format(svc[name]) + temp = 'lp:charms/{}' + svc['location'] = temp.format(svc['name']) + else: + for svc in other_services: + if svc['name'] in base_charms: + temp = 'lp:charms/{}' + svc['location'] = temp.format(svc['name']) else: - temp = 'lp:~openstack-charmers/charms/trusty/{}/next' - location = temp.format(svc[name]) - updated_services.append(svc + (location,)) - other_services = updated_services + temp = 'lp:~openstack-charmers/charms/{}/{}/next' + svc['location'] = temp.format(self.current_next, + svc['name']) return other_services def _add_services(self, this_service, other_services): """Add services to the deployment and set openstack-origin/source.""" - name = 0 other_services = self._determine_branch_locations(other_services) + super(OpenStackAmuletDeployment, self)._add_services(this_service, other_services) + services = other_services services.append(this_service) use_source = ['mysql', 'mongodb', 'rabbitmq-server', 'ceph'] if self.openstack: for svc in services: - if svc[name] not in use_source: + if svc['name'] not in use_source: config = {'openstack-origin': self.openstack} - self.d.configure(svc[name], config) + self.d.configure(svc['name'], config) if self.source: for svc in services: - if svc[name] in use_source: + if svc['name'] in use_source: config = {'source': self.source} - self.d.configure(svc[name], config) + self.d.configure(svc['name'], config) def _configure_services(self, configs): """Configure all of the services."""