Sync charm-helpers for Rocky series support

Change-Id: I689df4e384ccaf65eaaa349f312fe27ed7c700fe
This commit is contained in:
Corey Bryant 2018-06-21 19:03:16 +00:00
parent 632a19550f
commit f1a8b4e474
11 changed files with 110 additions and 15 deletions

View File

@ -13,6 +13,7 @@
# limitations under the License. # limitations under the License.
''' Helpers for interacting with OpenvSwitch ''' ''' Helpers for interacting with OpenvSwitch '''
import hashlib
import subprocess import subprocess
import os import os
import six import six
@ -39,6 +40,8 @@ iface {linuxbridge_port} inet manual
down ip link del {linuxbridge_port} down ip link del {linuxbridge_port}
""" """
MAX_KERNEL_INTERFACE_NAME_LEN = 15
def add_bridge(name, datapath_type=None): def add_bridge(name, datapath_type=None):
''' Add the named bridge to openvswitch ''' ''' Add the named bridge to openvswitch '''
@ -92,16 +95,39 @@ def add_ovsbridge_linuxbridge(name, bridge):
apt_install('python3-netifaces', fatal=True) apt_install('python3-netifaces', fatal=True)
import netifaces 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 ovsbridge_port = "veth-" + name
linuxbridge_port = "veth-" + bridge linuxbridge_port = "veth-" + bridge
log('Adding linuxbridge {} to ovsbridge {}'.format(bridge, name), if (len(ovsbridge_port) > MAX_KERNEL_INTERFACE_NAME_LEN or
level=INFO) 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() interfaces = netifaces.interfaces()
for interface in interfaces: for interface in interfaces:
if interface == ovsbridge_port or interface == linuxbridge_port: if interface == ovsbridge_port or interface == linuxbridge_port:
log('Interface {} already exists'.format(interface), level=INFO) log('Interface {} already exists'.format(interface), level=INFO)
return return
log('Adding linuxbridge {} to ovsbridge {}'.format(bridge, name),
level=INFO)
check_for_eni_source() check_for_eni_source()
with open('/etc/network/interfaces.d/{}.cfg'.format( with open('/etc/network/interfaces.d/{}.cfg'.format(
@ -134,6 +160,20 @@ def set_manager(manager):
'ssl:{}'.format(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' CERT_PATH = '/etc/openvswitch/ovsclient-cert.pem'
@ -194,3 +234,16 @@ def disable_ipfix(bridge):
''' '''
cmd = ['ovs-vsctl', 'clear', 'Bridge', bridge, 'ipfix'] cmd = ['ovs-vsctl', 'clear', 'Bridge', bridge, 'ipfix']
subprocess.check_call(cmd) 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

View File

@ -291,6 +291,8 @@ class OpenStackAmuletDeployment(AmuletDeployment):
('zesty', None): self.zesty_ocata, ('zesty', None): self.zesty_ocata,
('artful', None): self.artful_pike, ('artful', None): self.artful_pike,
('bionic', None): self.bionic_queens, ('bionic', None): self.bionic_queens,
('bionic', 'cloud:bionic-rocky'): self.bionic_rocky,
('cosmic', None): self.cosmic_rocky,
} }
return releases[(self.series, self.openstack)] return releases[(self.series, self.openstack)]
@ -306,6 +308,7 @@ class OpenStackAmuletDeployment(AmuletDeployment):
('zesty', 'ocata'), ('zesty', 'ocata'),
('artful', 'pike'), ('artful', 'pike'),
('bionic', 'queens'), ('bionic', 'queens'),
('cosmic', 'rocky'),
]) ])
if self.openstack: if self.openstack:
os_origin = self.openstack.split(':')[1] os_origin = self.openstack.split(':')[1]

View File

@ -56,7 +56,7 @@ OPENSTACK_RELEASES_PAIRS = [
'trusty_mitaka', 'xenial_mitaka', 'xenial_newton', 'trusty_mitaka', 'xenial_mitaka', 'xenial_newton',
'yakkety_newton', 'xenial_ocata', 'zesty_ocata', 'yakkety_newton', 'xenial_ocata', 'zesty_ocata',
'xenial_pike', 'artful_pike', 'xenial_queens', 'xenial_pike', 'artful_pike', 'xenial_queens',
'bionic_queens'] 'bionic_queens', 'bionic_rocky', 'cosmic_rocky']
class OpenStackAmuletUtils(AmuletUtils): class OpenStackAmuletUtils(AmuletUtils):

View File

@ -190,8 +190,8 @@ class OSContextGenerator(object):
class SharedDBContext(OSContextGenerator): class SharedDBContext(OSContextGenerator):
interfaces = ['shared-db'] interfaces = ['shared-db']
def __init__(self, def __init__(self, database=None, user=None, relation_prefix=None,
database=None, user=None, relation_prefix=None, ssl_dir=None): ssl_dir=None, relation_id=None):
"""Allows inspecting relation for settings prefixed with """Allows inspecting relation for settings prefixed with
relation_prefix. This is useful for parsing access for multiple relation_prefix. This is useful for parsing access for multiple
databases returned via the shared-db interface (eg, nova_password, databases returned via the shared-db interface (eg, nova_password,
@ -202,6 +202,7 @@ class SharedDBContext(OSContextGenerator):
self.user = user self.user = user
self.ssl_dir = ssl_dir self.ssl_dir = ssl_dir
self.rel_name = self.interfaces[0] self.rel_name = self.interfaces[0]
self.relation_id = relation_id
def __call__(self): def __call__(self):
self.database = self.database or config('database') self.database = self.database or config('database')
@ -235,7 +236,12 @@ class SharedDBContext(OSContextGenerator):
if self.relation_prefix: if self.relation_prefix:
password_setting = self.relation_prefix + '_password' 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 self.related = True
for unit in related_units(rid): for unit in related_units(rid):
rdata = relation_get(rid=rid, unit=unit) rdata = relation_get(rid=rid, unit=unit)
@ -448,11 +454,13 @@ class IdentityCredentialsContext(IdentityServiceContext):
class AMQPContext(OSContextGenerator): 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.ssl_dir = ssl_dir
self.rel_name = rel_name self.rel_name = rel_name
self.relation_prefix = relation_prefix self.relation_prefix = relation_prefix
self.interfaces = [rel_name] self.interfaces = [rel_name]
self.relation_id = relation_id
def __call__(self): def __call__(self):
log('Generating template context for amqp', level=DEBUG) log('Generating template context for amqp', level=DEBUG)
@ -473,7 +481,11 @@ class AMQPContext(OSContextGenerator):
raise OSContextError raise OSContextError
ctxt = {} 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 ha_vip_only = False
self.related = True self.related = True
transport_hosts = None transport_hosts = None
@ -1506,10 +1518,6 @@ class NeutronAPIContext(OSContextGenerator):
'rel_key': 'enable-qos', 'rel_key': 'enable-qos',
'default': False, 'default': False,
}, },
'enable_vlan_trunking': {
'rel_key': 'enable-vlan-trunking',
'default': False,
},
} }
ctxt = self.get_neutron_options({}) ctxt = self.get_neutron_options({})
for rid in relation_ids('neutron-plugin-api'): for rid in relation_ids('neutron-plugin-api'):

View File

@ -133,6 +133,7 @@ UBUNTU_OPENSTACK_RELEASE = OrderedDict([
('zesty', 'ocata'), ('zesty', 'ocata'),
('artful', 'pike'), ('artful', 'pike'),
('bionic', 'queens'), ('bionic', 'queens'),
('cosmic', 'rocky'),
]) ])
@ -151,6 +152,7 @@ OPENSTACK_CODENAMES = OrderedDict([
('2017.1', 'ocata'), ('2017.1', 'ocata'),
('2017.2', 'pike'), ('2017.2', 'pike'),
('2018.1', 'queens'), ('2018.1', 'queens'),
('2018.2', 'rocky'),
]) ])
# The ugly duckling - must list releases oldest to newest # The ugly duckling - must list releases oldest to newest
@ -183,6 +185,8 @@ SWIFT_CODENAMES = OrderedDict([
['2.13.0', '2.15.0']), ['2.13.0', '2.15.0']),
('queens', ('queens',
['2.16.0', '2.17.0']), ['2.16.0', '2.17.0']),
('rocky',
['2.18.0']),
]) ])
# >= Liberty version->codename mapping # >= Liberty version->codename mapping

View File

@ -972,6 +972,13 @@ def application_version_set(version):
log("Application Version: {}".format(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) @translate_exc(from_exc=OSError, to_exc=NotImplementedError)
def is_leader(): def is_leader():
"""Does the current unit hold the juju leadership """Does the current unit hold the juju leadership

View File

@ -158,6 +158,14 @@ CLOUD_ARCHIVE_POCKETS = {
'queens/proposed': 'xenial-proposed/queens', 'queens/proposed': 'xenial-proposed/queens',
'xenial-queens/proposed': 'xenial-proposed/queens', 'xenial-queens/proposed': 'xenial-proposed/queens',
'xenial-proposed/queens': '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',
} }

View File

@ -50,7 +50,8 @@ class AmuletDeployment(object):
this_service['units'] = 1 this_service['units'] = 1
self.d.add(this_service['name'], units=this_service['units'], 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: for svc in other_services:
if 'location' in svc: if 'location' in svc:
@ -64,7 +65,8 @@ class AmuletDeployment(object):
svc['units'] = 1 svc['units'] = 1
self.d.add(svc['name'], charm=branch_location, units=svc['units'], 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): def _add_relations(self, relations):
"""Add all of the relations for the services.""" """Add all of the relations for the services."""

View File

@ -291,6 +291,8 @@ class OpenStackAmuletDeployment(AmuletDeployment):
('zesty', None): self.zesty_ocata, ('zesty', None): self.zesty_ocata,
('artful', None): self.artful_pike, ('artful', None): self.artful_pike,
('bionic', None): self.bionic_queens, ('bionic', None): self.bionic_queens,
('bionic', 'cloud:bionic-rocky'): self.bionic_rocky,
('cosmic', None): self.cosmic_rocky,
} }
return releases[(self.series, self.openstack)] return releases[(self.series, self.openstack)]
@ -306,6 +308,7 @@ class OpenStackAmuletDeployment(AmuletDeployment):
('zesty', 'ocata'), ('zesty', 'ocata'),
('artful', 'pike'), ('artful', 'pike'),
('bionic', 'queens'), ('bionic', 'queens'),
('cosmic', 'rocky'),
]) ])
if self.openstack: if self.openstack:
os_origin = self.openstack.split(':')[1] os_origin = self.openstack.split(':')[1]

View File

@ -56,7 +56,7 @@ OPENSTACK_RELEASES_PAIRS = [
'trusty_mitaka', 'xenial_mitaka', 'xenial_newton', 'trusty_mitaka', 'xenial_mitaka', 'xenial_newton',
'yakkety_newton', 'xenial_ocata', 'zesty_ocata', 'yakkety_newton', 'xenial_ocata', 'zesty_ocata',
'xenial_pike', 'artful_pike', 'xenial_queens', 'xenial_pike', 'artful_pike', 'xenial_queens',
'bionic_queens'] 'bionic_queens', 'bionic_rocky', 'cosmic_rocky']
class OpenStackAmuletUtils(AmuletUtils): class OpenStackAmuletUtils(AmuletUtils):

View File

@ -972,6 +972,13 @@ def application_version_set(version):
log("Application Version: {}".format(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) @translate_exc(from_exc=OSError, to_exc=NotImplementedError)
def is_leader(): def is_leader():
"""Does the current unit hold the juju leadership """Does the current unit hold the juju leadership