diff --git a/charmhelpers/contrib/charmsupport/nrpe.py b/charmhelpers/contrib/charmsupport/nrpe.py
index 10d86ac074a785e2cccd20148eef0521bef42957..f59fdd6ba341278667891094799f7336a87d48ff 100644
--- a/charmhelpers/contrib/charmsupport/nrpe.py
+++ b/charmhelpers/contrib/charmsupport/nrpe.py
@@ -305,7 +305,7 @@ class NRPE(object):
 
         # update-status hooks are configured to firing every 5 minutes by
         # default. When nagios-nrpe-server is restarted, the nagios server
-        # reports checks failing causing unneccessary alerts. Let's not restart
+        # reports checks failing causing unnecessary alerts. Let's not restart
         # on update-status hooks.
         if not hook_name() == 'update-status':
             service('restart', 'nagios-nrpe-server')
diff --git a/charmhelpers/contrib/openstack/amulet/deployment.py b/charmhelpers/contrib/openstack/amulet/deployment.py
index 5b7e3cfb6ade7b404bed4c5232e3fef9a0a02215..d1270a73423f079d036e6e8300c665c594cc23cd 100644
--- a/charmhelpers/contrib/openstack/amulet/deployment.py
+++ b/charmhelpers/contrib/openstack/amulet/deployment.py
@@ -293,7 +293,9 @@ class OpenStackAmuletDeployment(AmuletDeployment):
             ('artful', None): self.artful_pike,
             ('bionic', None): self.bionic_queens,
             ('bionic', 'cloud:bionic-rocky'): self.bionic_rocky,
+            ('bionic', 'cloud:bionic-stein'): self.bionic_stein,
             ('cosmic', None): self.cosmic_rocky,
+            ('disco', None): self.disco_stein,
         }
         return releases[(self.series, self.openstack)]
 
diff --git a/charmhelpers/contrib/openstack/amulet/utils.py b/charmhelpers/contrib/openstack/amulet/utils.py
index 9133e9b3d25a90a045c81a23934a17e9c5ad4e33..ea1fd8f397dab6b5d61c41fe35c56c46160de7b2 100644
--- a/charmhelpers/contrib/openstack/amulet/utils.py
+++ b/charmhelpers/contrib/openstack/amulet/utils.py
@@ -57,7 +57,8 @@ OPENSTACK_RELEASES_PAIRS = [
     'trusty_mitaka', 'xenial_mitaka', 'xenial_newton',
     'yakkety_newton', 'xenial_ocata', 'zesty_ocata',
     'xenial_pike', 'artful_pike', 'xenial_queens',
-    'bionic_queens', 'bionic_rocky', 'cosmic_rocky']
+    'bionic_queens', 'bionic_rocky', 'cosmic_rocky',
+    'bionic_stein', 'disco_stein']
 
 
 class OpenStackAmuletUtils(AmuletUtils):
diff --git a/charmhelpers/contrib/openstack/cert_utils.py b/charmhelpers/contrib/openstack/cert_utils.py
index 3e078703b124994be2467c167e1df51fd3e350fd..3a3c6de7224f47c6be61b2e0246429ff72ed86d3 100644
--- a/charmhelpers/contrib/openstack/cert_utils.py
+++ b/charmhelpers/contrib/openstack/cert_utils.py
@@ -195,7 +195,7 @@ def install_certs(ssl_dir, certs, chain=None):
         if chain:
             # Append chain file so that clients that trust the root CA will
             # trust certs signed by an intermediate in the chain
-            cert_data = cert_data + chain
+            cert_data = cert_data + os.linesep + chain
         write_file(
             path=os.path.join(ssl_dir, cert_filename),
             content=cert_data, perms=0o640)
diff --git a/charmhelpers/contrib/openstack/context.py b/charmhelpers/contrib/openstack/context.py
index 72084cb38e627d4d9ab74c63d1529c212b579820..614d444b0d9160128964f068846dd56b9b44f4d5 100644
--- a/charmhelpers/contrib/openstack/context.py
+++ b/charmhelpers/contrib/openstack/context.py
@@ -98,7 +98,6 @@ from charmhelpers.contrib.network.ip import (
 from charmhelpers.contrib.openstack.utils import (
     config_flags_parser,
     enable_memcache,
-    snap_install_requested,
     CompareOpenStackReleases,
     os_release,
 )
@@ -252,13 +251,8 @@ class SharedDBContext(OSContextGenerator):
                     'database': self.database,
                     'database_user': self.user,
                     'database_password': rdata.get(password_setting),
-                    'database_type': 'mysql'
+                    'database_type': 'mysql+pymysql'
                 }
-                # Note(coreycb): We can drop mysql+pymysql if we want when the
-                # following review lands, though it seems mysql+pymysql would
-                # be preferred. https://review.openstack.org/#/c/462190/
-                if snap_install_requested():
-                    ctxt['database_type'] = 'mysql+pymysql'
                 if self.context_complete(ctxt):
                     db_ssl(rdata, ctxt, self.ssl_dir)
                     return ctxt
diff --git a/charmhelpers/contrib/openstack/ha/utils.py b/charmhelpers/contrib/openstack/ha/utils.py
index cdf4b4c92110f047f393ad31300e42d2b67855ba..718c6d659c734323e19241954a32db8acd3c1513 100644
--- a/charmhelpers/contrib/openstack/ha/utils.py
+++ b/charmhelpers/contrib/openstack/ha/utils.py
@@ -63,6 +63,9 @@ JSON_ENCODE_OPTIONS = dict(
     separators=(',', ':'),
 )
 
+VIP_GROUP_NAME = 'grp_{service}_vips'
+DNSHA_GROUP_NAME = 'grp_{service}_hostnames'
+
 
 class DNSHAException(Exception):
     """Raised when an error occurs setting up DNS HA
@@ -239,7 +242,7 @@ def update_hacluster_dns_ha(service, relation_data,
             'Informing the ha relation'.format(' '.join(hostname_group)),
             DEBUG)
         relation_data['groups'] = {
-            'grp_{}_hostnames'.format(service): ' '.join(hostname_group)
+            DNSHA_GROUP_NAME.format(service=service): ' '.join(hostname_group)
         }
     else:
         msg = 'DNS HA: Hostname group has no members.'
@@ -247,6 +250,27 @@ def update_hacluster_dns_ha(service, relation_data,
         raise DNSHAException(msg)
 
 
+def get_vip_settings(vip):
+    """Calculate which nic is on the correct network for the given vip.
+
+    If nic or netmask discovery fail then fallback to using charm supplied
+    config. If fallback is used this is indicated via the fallback variable.
+
+    @param vip: VIP to lookup nic and cidr for.
+    @returns (str, str, bool): eg (iface, netmask, fallback)
+    """
+    iface = get_iface_for_address(vip)
+    netmask = get_netmask_for_address(vip)
+    fallback = False
+    if iface is None:
+        iface = config('vip_iface')
+        fallback = True
+    if netmask is None:
+        netmask = config('vip_cidr')
+        fallback = True
+    return iface, netmask, fallback
+
+
 def update_hacluster_vip(service, relation_data):
     """ Configure VIP resources based on provided configuration
 
@@ -264,17 +288,9 @@ def update_hacluster_vip(service, relation_data):
             res_vip = 'ocf:heartbeat:IPaddr2'
             vip_params = 'ip'
 
-        iface = get_iface_for_address(vip)
-        netmask = get_netmask_for_address(vip)
-
-        fallback_params = False
-        if iface is None:
-            iface = config('vip_iface')
-            fallback_params = True
-        if netmask is None:
-            netmask = config('vip_cidr')
-            fallback_params = True
+        iface, netmask, fallback = get_vip_settings(vip)
 
+        vip_monitoring = 'op monitor depth="0" timeout="20s" interval="10s"'
         if iface is not None:
             # NOTE(jamespage): Delete old VIP resources
             # Old style naming encoding iface in name
@@ -293,14 +309,15 @@ def update_hacluster_vip(service, relation_data):
             # NOTE(jamespage):
             # Use option provided vip params if these where used
             # instead of auto-detected values
-            if fallback_params:
+            if fallback:
                 relation_data['resource_params'][vip_key] = (
                     'params {ip}="{vip}" cidr_netmask="{netmask}" '
-                    'nic="{iface}"'.format(ip=vip_params,
-                                           vip=vip,
-                                           iface=iface,
-                                           netmask=netmask)
-                )
+                    'nic="{iface}" {vip_monitoring}'.format(
+                        ip=vip_params,
+                        vip=vip,
+                        iface=iface,
+                        netmask=netmask,
+                        vip_monitoring=vip_monitoring))
             else:
                 # NOTE(jamespage):
                 # let heartbeat figure out which interface and
@@ -308,8 +325,10 @@ def update_hacluster_vip(service, relation_data):
                 # when network interface naming is not
                 # consistent across units.
                 relation_data['resource_params'][vip_key] = (
-                    'params {ip}="{vip}"'.format(ip=vip_params,
-                                                 vip=vip))
+                    'params {ip}="{vip}" {vip_monitoring}'.format(
+                        ip=vip_params,
+                        vip=vip,
+                        vip_monitoring=vip_monitoring))
 
             vip_group.append(vip_key)
 
@@ -320,7 +339,7 @@ def update_hacluster_vip(service, relation_data):
             relation_data['delete_resources'] = vips_to_delete
 
     if len(vip_group) >= 1:
-        key = 'grp_{}_vips'.format(service)
+        key = VIP_GROUP_NAME.format(service=service)
         try:
             relation_data['groups'][key] = ' '.join(vip_group)
         except KeyError:
diff --git a/charmhelpers/contrib/openstack/utils.py b/charmhelpers/contrib/openstack/utils.py
index 59312fcff4d11b1d5eed4569607a8e676924f216..4e432a25bd2e00ab4bf5fa9ba6d05457fce99c7b 100644
--- a/charmhelpers/contrib/openstack/utils.py
+++ b/charmhelpers/contrib/openstack/utils.py
@@ -118,6 +118,7 @@ OPENSTACK_RELEASES = (
     'pike',
     'queens',
     'rocky',
+    'stein',
 )
 
 UBUNTU_OPENSTACK_RELEASE = OrderedDict([
@@ -136,6 +137,7 @@ UBUNTU_OPENSTACK_RELEASE = OrderedDict([
     ('artful', 'pike'),
     ('bionic', 'queens'),
     ('cosmic', 'rocky'),
+    ('disco', 'stein'),
 ])
 
 
@@ -155,6 +157,7 @@ OPENSTACK_CODENAMES = OrderedDict([
     ('2017.2', 'pike'),
     ('2018.1', 'queens'),
     ('2018.2', 'rocky'),
+    ('2019.1', 'stein'),
 ])
 
 # The ugly duckling - must list releases oldest to newest
@@ -189,6 +192,8 @@ SWIFT_CODENAMES = OrderedDict([
         ['2.16.0', '2.17.0']),
     ('rocky',
         ['2.18.0', '2.19.0']),
+    ('stein',
+        ['2.19.0']),
 ])
 
 # >= Liberty version->codename mapping
@@ -201,6 +206,7 @@ PACKAGE_CODENAMES = {
         ('16', 'pike'),
         ('17', 'queens'),
         ('18', 'rocky'),
+        ('19', 'stein'),
     ]),
     'neutron-common': OrderedDict([
         ('7', 'liberty'),
@@ -210,6 +216,7 @@ PACKAGE_CODENAMES = {
         ('11', 'pike'),
         ('12', 'queens'),
         ('13', 'rocky'),
+        ('14', 'stein'),
     ]),
     'cinder-common': OrderedDict([
         ('7', 'liberty'),
@@ -219,6 +226,7 @@ PACKAGE_CODENAMES = {
         ('11', 'pike'),
         ('12', 'queens'),
         ('13', 'rocky'),
+        ('14', 'stein'),
     ]),
     'keystone': OrderedDict([
         ('8', 'liberty'),
@@ -228,6 +236,7 @@ PACKAGE_CODENAMES = {
         ('12', 'pike'),
         ('13', 'queens'),
         ('14', 'rocky'),
+        ('15', 'stein'),
     ]),
     'horizon-common': OrderedDict([
         ('8', 'liberty'),
@@ -237,6 +246,7 @@ PACKAGE_CODENAMES = {
         ('12', 'pike'),
         ('13', 'queens'),
         ('14', 'rocky'),
+        ('15', 'stein'),
     ]),
     'ceilometer-common': OrderedDict([
         ('5', 'liberty'),
@@ -246,6 +256,7 @@ PACKAGE_CODENAMES = {
         ('9', 'pike'),
         ('10', 'queens'),
         ('11', 'rocky'),
+        ('12', 'stein'),
     ]),
     'heat-common': OrderedDict([
         ('5', 'liberty'),
@@ -255,6 +266,7 @@ PACKAGE_CODENAMES = {
         ('9', 'pike'),
         ('10', 'queens'),
         ('11', 'rocky'),
+        ('12', 'stein'),
     ]),
     'glance-common': OrderedDict([
         ('11', 'liberty'),
@@ -264,6 +276,7 @@ PACKAGE_CODENAMES = {
         ('15', 'pike'),
         ('16', 'queens'),
         ('17', 'rocky'),
+        ('18', 'stein'),
     ]),
     'openstack-dashboard': OrderedDict([
         ('8', 'liberty'),
@@ -273,6 +286,7 @@ PACKAGE_CODENAMES = {
         ('12', 'pike'),
         ('13', 'queens'),
         ('14', 'rocky'),
+        ('15', 'stein'),
     ]),
 }
 
diff --git a/charmhelpers/fetch/ubuntu.py b/charmhelpers/fetch/ubuntu.py
index c7ad128c785a805784e3d913e7b45b9dbd98daf0..8a5cadf1dc37122afb2269c03505450ebf5b2199 100644
--- a/charmhelpers/fetch/ubuntu.py
+++ b/charmhelpers/fetch/ubuntu.py
@@ -166,6 +166,14 @@ CLOUD_ARCHIVE_POCKETS = {
     'rocky/proposed': 'bionic-proposed/rocky',
     'bionic-rocky/proposed': 'bionic-proposed/rocky',
     'bionic-proposed/rocky': 'bionic-proposed/rocky',
+    # Stein
+    'stein': 'bionic-updates/stein',
+    'bionic-stein': 'bionic-updates/stein',
+    'bionic-stein/updates': 'bionic-updates/stein',
+    'bionic-updates/stein': 'bionic-updates/stein',
+    'stein/proposed': 'bionic-proposed/stein',
+    'bionic-stein/proposed': 'bionic-proposed/stein',
+    'bionic-proposed/stein': 'bionic-proposed/stein',
 }