diff --git a/hooks/charmhelpers/contrib/network/ovs/__init__.py b/hooks/charmhelpers/contrib/network/ovs/__init__.py index 9b3583f3..a8856e9c 100644 --- a/hooks/charmhelpers/contrib/network/ovs/__init__.py +++ b/hooks/charmhelpers/contrib/network/ovs/__init__.py @@ -13,6 +13,7 @@ # limitations under the License. ''' Helpers for interacting with OpenvSwitch ''' +import hashlib import subprocess import os import six @@ -39,6 +40,8 @@ iface {linuxbridge_port} inet manual down ip link del {linuxbridge_port} """ +MAX_KERNEL_INTERFACE_NAME_LEN = 15 + def add_bridge(name, datapath_type=None): ''' Add the named bridge to openvswitch ''' @@ -92,16 +95,39 @@ def add_ovsbridge_linuxbridge(name, bridge): apt_install('python3-netifaces', fatal=True) import netifaces + # NOTE(jamespage): + # Older code supported addition of a linuxbridge directly + # to an OVS bridge; ensure we don't break uses on upgrade + existing_ovs_bridge = port_to_br(bridge) + if existing_ovs_bridge is not None: + log('Linuxbridge {} is already directly in use' + ' by OVS bridge {}'.format(bridge, existing_ovs_bridge), + level=INFO) + return + + # NOTE(jamespage): + # preserve existing naming because interfaces may already exist. ovsbridge_port = "veth-" + name linuxbridge_port = "veth-" + bridge - log('Adding linuxbridge {} to ovsbridge {}'.format(bridge, name), - level=INFO) + if (len(ovsbridge_port) > MAX_KERNEL_INTERFACE_NAME_LEN or + len(linuxbridge_port) > MAX_KERNEL_INTERFACE_NAME_LEN): + # NOTE(jamespage): + # use parts of hashed bridgename (openstack style) when + # a bridge name exceeds 15 chars + hashed_bridge = hashlib.sha256(bridge.encode('UTF-8')).hexdigest() + base = '{}-{}'.format(hashed_bridge[:8], hashed_bridge[-2:]) + ovsbridge_port = "cvo{}".format(base) + linuxbridge_port = "cvb{}".format(base) + interfaces = netifaces.interfaces() for interface in interfaces: if interface == ovsbridge_port or interface == linuxbridge_port: log('Interface {} already exists'.format(interface), level=INFO) return + log('Adding linuxbridge {} to ovsbridge {}'.format(bridge, name), + level=INFO) + check_for_eni_source() with open('/etc/network/interfaces.d/{}.cfg'.format( @@ -134,6 +160,20 @@ def set_manager(manager): 'ssl:{}'.format(manager)]) +def set_Open_vSwitch_column_value(column_value): + """ + Calls ovs-vsctl and sets the 'column_value' in the Open_vSwitch table. + + :param column_value: + See http://www.openvswitch.org//ovs-vswitchd.conf.db.5.pdf for + details of the relevant values. + :type str + :raises CalledProcessException: possibly ovsdb-server is not running + """ + log('Setting {} in the Open_vSwitch table'.format(column_value)) + subprocess.check_call(['ovs-vsctl', 'set', 'Open_vSwitch', '.', column_value]) + + CERT_PATH = '/etc/openvswitch/ovsclient-cert.pem' @@ -194,3 +234,16 @@ def disable_ipfix(bridge): ''' cmd = ['ovs-vsctl', 'clear', 'Bridge', bridge, 'ipfix'] subprocess.check_call(cmd) + + +def port_to_br(port): + '''Determine the bridge that contains a port + :param port: Name of port to check for + :returns str: OVS bridge containing port or None if not found + ''' + try: + return subprocess.check_output( + ['ovs-vsctl', 'port-to-br', port] + ).decode('UTF-8').strip() + except subprocess.CalledProcessError: + return None diff --git a/hooks/charmhelpers/contrib/openstack/amulet/deployment.py b/hooks/charmhelpers/contrib/openstack/amulet/deployment.py index 66beeda2..1c96752a 100644 --- a/hooks/charmhelpers/contrib/openstack/amulet/deployment.py +++ b/hooks/charmhelpers/contrib/openstack/amulet/deployment.py @@ -291,6 +291,8 @@ class OpenStackAmuletDeployment(AmuletDeployment): ('zesty', None): self.zesty_ocata, ('artful', None): self.artful_pike, ('bionic', None): self.bionic_queens, + ('bionic', 'cloud:bionic-rocky'): self.bionic_rocky, + ('cosmic', None): self.cosmic_rocky, } return releases[(self.series, self.openstack)] @@ -306,6 +308,7 @@ class OpenStackAmuletDeployment(AmuletDeployment): ('zesty', 'ocata'), ('artful', 'pike'), ('bionic', 'queens'), + ('cosmic', 'rocky'), ]) if self.openstack: os_origin = self.openstack.split(':')[1] diff --git a/hooks/charmhelpers/contrib/openstack/amulet/utils.py b/hooks/charmhelpers/contrib/openstack/amulet/utils.py index d43038b2..ef4ab54b 100644 --- a/hooks/charmhelpers/contrib/openstack/amulet/utils.py +++ b/hooks/charmhelpers/contrib/openstack/amulet/utils.py @@ -56,7 +56,7 @@ OPENSTACK_RELEASES_PAIRS = [ 'trusty_mitaka', 'xenial_mitaka', 'xenial_newton', 'yakkety_newton', 'xenial_ocata', 'zesty_ocata', 'xenial_pike', 'artful_pike', 'xenial_queens', - 'bionic_queens'] + 'bionic_queens', 'bionic_rocky', 'cosmic_rocky'] class OpenStackAmuletUtils(AmuletUtils): diff --git a/hooks/charmhelpers/contrib/openstack/context.py b/hooks/charmhelpers/contrib/openstack/context.py index 98a9d4a8..f3741b0e 100644 --- a/hooks/charmhelpers/contrib/openstack/context.py +++ b/hooks/charmhelpers/contrib/openstack/context.py @@ -190,8 +190,8 @@ class OSContextGenerator(object): class SharedDBContext(OSContextGenerator): interfaces = ['shared-db'] - def __init__(self, - database=None, user=None, relation_prefix=None, ssl_dir=None): + def __init__(self, database=None, user=None, relation_prefix=None, + ssl_dir=None, relation_id=None): """Allows inspecting relation for settings prefixed with relation_prefix. This is useful for parsing access for multiple databases returned via the shared-db interface (eg, nova_password, @@ -202,6 +202,7 @@ class SharedDBContext(OSContextGenerator): self.user = user self.ssl_dir = ssl_dir self.rel_name = self.interfaces[0] + self.relation_id = relation_id def __call__(self): self.database = self.database or config('database') @@ -235,7 +236,12 @@ class SharedDBContext(OSContextGenerator): if self.relation_prefix: password_setting = self.relation_prefix + '_password' - for rid in relation_ids(self.interfaces[0]): + if self.relation_id: + rids = [self.relation_id] + else: + rids = relation_ids(self.interfaces[0]) + + for rid in rids: self.related = True for unit in related_units(rid): rdata = relation_get(rid=rid, unit=unit) @@ -448,11 +454,13 @@ class IdentityCredentialsContext(IdentityServiceContext): class AMQPContext(OSContextGenerator): - def __init__(self, ssl_dir=None, rel_name='amqp', relation_prefix=None): + def __init__(self, ssl_dir=None, rel_name='amqp', relation_prefix=None, + relation_id=None): self.ssl_dir = ssl_dir self.rel_name = rel_name self.relation_prefix = relation_prefix self.interfaces = [rel_name] + self.relation_id = relation_id def __call__(self): log('Generating template context for amqp', level=DEBUG) @@ -473,7 +481,11 @@ class AMQPContext(OSContextGenerator): raise OSContextError ctxt = {} - for rid in relation_ids(self.rel_name): + if self.relation_id: + rids = [self.relation_id] + else: + rids = relation_ids(self.rel_name) + for rid in rids: ha_vip_only = False self.related = True transport_hosts = None @@ -1506,10 +1518,6 @@ class NeutronAPIContext(OSContextGenerator): 'rel_key': 'enable-qos', 'default': False, }, - 'enable_vlan_trunking': { - 'rel_key': 'enable-vlan-trunking', - 'default': False, - }, } ctxt = self.get_neutron_options({}) for rid in relation_ids('neutron-plugin-api'): diff --git a/hooks/charmhelpers/contrib/openstack/utils.py b/hooks/charmhelpers/contrib/openstack/utils.py index 6184abd0..0180e555 100644 --- a/hooks/charmhelpers/contrib/openstack/utils.py +++ b/hooks/charmhelpers/contrib/openstack/utils.py @@ -133,6 +133,7 @@ UBUNTU_OPENSTACK_RELEASE = OrderedDict([ ('zesty', 'ocata'), ('artful', 'pike'), ('bionic', 'queens'), + ('cosmic', 'rocky'), ]) @@ -151,6 +152,7 @@ OPENSTACK_CODENAMES = OrderedDict([ ('2017.1', 'ocata'), ('2017.2', 'pike'), ('2018.1', 'queens'), + ('2018.2', 'rocky'), ]) # The ugly duckling - must list releases oldest to newest @@ -183,6 +185,8 @@ SWIFT_CODENAMES = OrderedDict([ ['2.13.0', '2.15.0']), ('queens', ['2.16.0', '2.17.0']), + ('rocky', + ['2.18.0']), ]) # >= Liberty version->codename mapping diff --git a/hooks/charmhelpers/core/hookenv.py b/hooks/charmhelpers/core/hookenv.py index 627d8f79..ed7af39e 100644 --- a/hooks/charmhelpers/core/hookenv.py +++ b/hooks/charmhelpers/core/hookenv.py @@ -972,6 +972,13 @@ def application_version_set(version): log("Application Version: {}".format(version)) +@translate_exc(from_exc=OSError, to_exc=NotImplementedError) +def goal_state(): + """Juju goal state values""" + cmd = ['goal-state', '--format=json'] + return json.loads(subprocess.check_output(cmd).decode('UTF-8')) + + @translate_exc(from_exc=OSError, to_exc=NotImplementedError) def is_leader(): """Does the current unit hold the juju leadership diff --git a/hooks/charmhelpers/fetch/ubuntu.py b/hooks/charmhelpers/fetch/ubuntu.py index 653d58f1..736be713 100644 --- a/hooks/charmhelpers/fetch/ubuntu.py +++ b/hooks/charmhelpers/fetch/ubuntu.py @@ -158,6 +158,14 @@ CLOUD_ARCHIVE_POCKETS = { 'queens/proposed': 'xenial-proposed/queens', 'xenial-queens/proposed': 'xenial-proposed/queens', 'xenial-proposed/queens': 'xenial-proposed/queens', + # Rocky + 'rocky': 'bionic-updates/rocky', + 'bionic-rocky': 'bionic-updates/rocky', + 'bionic-rocky/updates': 'bionic-updates/rocky', + 'bionic-updates/rocky': 'bionic-updates/rocky', + 'rocky/proposed': 'bionic-proposed/rocky', + 'bionic-rocky/proposed': 'bionic-proposed/rocky', + 'bionic-proposed/rocky': 'bionic-proposed/rocky', } diff --git a/tests/charmhelpers/contrib/amulet/deployment.py b/tests/charmhelpers/contrib/amulet/deployment.py index 9c65518e..d21d01d8 100644 --- a/tests/charmhelpers/contrib/amulet/deployment.py +++ b/tests/charmhelpers/contrib/amulet/deployment.py @@ -50,7 +50,8 @@ class AmuletDeployment(object): this_service['units'] = 1 self.d.add(this_service['name'], units=this_service['units'], - constraints=this_service.get('constraints')) + constraints=this_service.get('constraints'), + storage=this_service.get('storage')) for svc in other_services: if 'location' in svc: @@ -64,7 +65,8 @@ class AmuletDeployment(object): svc['units'] = 1 self.d.add(svc['name'], charm=branch_location, units=svc['units'], - constraints=svc.get('constraints')) + constraints=svc.get('constraints'), + storage=svc.get('storage')) 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 66beeda2..1c96752a 100644 --- a/tests/charmhelpers/contrib/openstack/amulet/deployment.py +++ b/tests/charmhelpers/contrib/openstack/amulet/deployment.py @@ -291,6 +291,8 @@ class OpenStackAmuletDeployment(AmuletDeployment): ('zesty', None): self.zesty_ocata, ('artful', None): self.artful_pike, ('bionic', None): self.bionic_queens, + ('bionic', 'cloud:bionic-rocky'): self.bionic_rocky, + ('cosmic', None): self.cosmic_rocky, } return releases[(self.series, self.openstack)] @@ -306,6 +308,7 @@ class OpenStackAmuletDeployment(AmuletDeployment): ('zesty', 'ocata'), ('artful', 'pike'), ('bionic', 'queens'), + ('cosmic', 'rocky'), ]) if self.openstack: os_origin = self.openstack.split(':')[1] diff --git a/tests/charmhelpers/contrib/openstack/amulet/utils.py b/tests/charmhelpers/contrib/openstack/amulet/utils.py index d43038b2..ef4ab54b 100644 --- a/tests/charmhelpers/contrib/openstack/amulet/utils.py +++ b/tests/charmhelpers/contrib/openstack/amulet/utils.py @@ -56,7 +56,7 @@ OPENSTACK_RELEASES_PAIRS = [ 'trusty_mitaka', 'xenial_mitaka', 'xenial_newton', 'yakkety_newton', 'xenial_ocata', 'zesty_ocata', 'xenial_pike', 'artful_pike', 'xenial_queens', - 'bionic_queens'] + 'bionic_queens', 'bionic_rocky', 'cosmic_rocky'] class OpenStackAmuletUtils(AmuletUtils): diff --git a/tests/charmhelpers/core/hookenv.py b/tests/charmhelpers/core/hookenv.py index 627d8f79..ed7af39e 100644 --- a/tests/charmhelpers/core/hookenv.py +++ b/tests/charmhelpers/core/hookenv.py @@ -972,6 +972,13 @@ def application_version_set(version): log("Application Version: {}".format(version)) +@translate_exc(from_exc=OSError, to_exc=NotImplementedError) +def goal_state(): + """Juju goal state values""" + cmd = ['goal-state', '--format=json'] + return json.loads(subprocess.check_output(cmd).decode('UTF-8')) + + @translate_exc(from_exc=OSError, to_exc=NotImplementedError) def is_leader(): """Does the current unit hold the juju leadership