diff --git a/SOURCES/0023-DatasourceEc2-add-warning-message-when-not-on-AWS.patch b/SOURCES/0023-DatasourceEc2-add-warning-message-when-not-on-AWS.patch
index 89ae3c3..df75290 100644
--- a/SOURCES/0023-DatasourceEc2-add-warning-message-when-not-on-AWS.patch
+++ b/SOURCES/0023-DatasourceEc2-add-warning-message-when-not-on-AWS.patch
@@ -1,4 +1,4 @@
-From 75ee377f902082f23de0feea190444e19a942420 Mon Sep 17 00:00:00 2001
+From aeaaf3f3b3f426a7906a2e03d924b2e42528c600 Mon Sep 17 00:00:00 2001
 From: Scott Moser <smoser@brickies.net>
 Date: Thu, 23 Feb 2017 17:15:27 -0500
 Subject: [PATCH 2/5] DatasourceEc2: add warning message when not on AWS.
@@ -8,7 +8,7 @@ will now warn once per instance.
 
 (cherry picked from commit 9bb55c6c45bcc5e310cf7e4d42cad53759dcca15)
 
-Resolves: rhbz#1496113
+Resolves: rhbz#1482547
 
 Signed-off-by: Ryan McCabe <rmccabe@redhat.com>
 ---
diff --git a/SOURCES/0024-Identify-Brightbox-as-an-Ec2-datasource-user.patch b/SOURCES/0024-Identify-Brightbox-as-an-Ec2-datasource-user.patch
index 148e7a1..4cdc8b2 100644
--- a/SOURCES/0024-Identify-Brightbox-as-an-Ec2-datasource-user.patch
+++ b/SOURCES/0024-Identify-Brightbox-as-an-Ec2-datasource-user.patch
@@ -1,4 +1,4 @@
-From 9044e39b1db9da242c244202ad649c5f8b05bc12 Mon Sep 17 00:00:00 2001
+From dcdb2961701ccb0025c8ab45fe76fbaa27ba8133 Mon Sep 17 00:00:00 2001
 From: Scott Moser <smoser@brickies.net>
 Date: Fri, 24 Feb 2017 14:19:20 -0500
 Subject: [PATCH 3/5] Identify Brightbox as an Ec2 datasource user.
@@ -9,7 +9,7 @@ product serial to a string that ends with 'brightbox.com'.
 LP: #1661693
 (cherry picked from commit 5dd5b2cb539a84ed59f2b3181020d2bd18989718)
 
-Resolves: rhbz#1496113
+Resolves: rhbz#1482547
 
 Signed-off-by: Ryan McCabe <rmccabe@redhat.com>
 ---
diff --git a/SOURCES/0025-AliYun-Enable-platform-identification-and-enable-by-.patch b/SOURCES/0025-AliYun-Enable-platform-identification-and-enable-by-.patch
index ac01a97..c7f475f 100644
--- a/SOURCES/0025-AliYun-Enable-platform-identification-and-enable-by-.patch
+++ b/SOURCES/0025-AliYun-Enable-platform-identification-and-enable-by-.patch
@@ -1,4 +1,4 @@
-From a7727ecf117a2bc02f68405823796afe1d76d3e3 Mon Sep 17 00:00:00 2001
+From 05561668e42d905cca7d72c2b80a939fbddb2c9d Mon Sep 17 00:00:00 2001
 From: Junjie Wang <jingni.wjj@alibaba-inc.com>
 Date: Fri, 21 Apr 2017 20:06:09 +0800
 Subject: [PATCH 4/5] AliYun: Enable platform identification and enable by
@@ -14,7 +14,7 @@ enable AliYun by default.
 LP: #1638931
 (cherry picked from commit 4a60af54957634920e84a928aa22b4fc9a6dfd11)
 
-Resolves: rhbz#1496113
+Resolves: rhbz#1482547
 
 Signed-off-by: Ryan McCabe <rmccabe@redhat.com>
 ---
diff --git a/SOURCES/0026-Fix-alibaba-cloud-unit-tests-to-work-with-0.7.9.patch b/SOURCES/0026-Fix-alibaba-cloud-unit-tests-to-work-with-0.7.9.patch
index 1755547..5e34868 100644
--- a/SOURCES/0026-Fix-alibaba-cloud-unit-tests-to-work-with-0.7.9.patch
+++ b/SOURCES/0026-Fix-alibaba-cloud-unit-tests-to-work-with-0.7.9.patch
@@ -1,9 +1,9 @@
-From b87c46fe008dc4df50b0103d598d218f8dd26735 Mon Sep 17 00:00:00 2001
+From 1a674be4e78d77ed40e3a07385c4cce617476129 Mon Sep 17 00:00:00 2001
 From: Ryan McCabe <rmccabe@redhat.com>
 Date: Tue, 5 Sep 2017 13:02:00 -0400
 Subject: [PATCH 5/5] Fix alibaba cloud unit tests to work with 0.7.9
 
-Resolves: rhbz#1496113
+Resolves: rhbz#1482547
 X-downstream-only: Yes
 
 Signed-off-by: Ryan McCabe <rmccabe@redhat.com>
diff --git a/SOURCES/0027-Fix-eni-rendering-of-multiple-IPs-per-interface.patch b/SOURCES/0027-Fix-eni-rendering-of-multiple-IPs-per-interface.patch
new file mode 100644
index 0000000..7767342
--- /dev/null
+++ b/SOURCES/0027-Fix-eni-rendering-of-multiple-IPs-per-interface.patch
@@ -0,0 +1,185 @@
+From 335d2b7270c151fd981d9e500f239ab75a59a4b3 Mon Sep 17 00:00:00 2001
+From: Ryan Harper <ryan.harper@canonical.com>
+Date: Wed, 25 Jan 2017 15:45:40 -0600
+Subject: [PATCH] Fix eni rendering of multiple IPs per interface
+
+The iface:alias syntax for eni rendering is brittle with ipv6.
+Replace it with using multiple iface stanzas with the same iface
+name which is supported.  Side-effect is that one can no longer
+do 'ifup $iface:$alias' but requires instead use of ip address
+{add|delete} instead.
+
+LP: #1657940
+(cherry picked from commit 2de1c247e285cce0b25ab70abdc56ccd41019c27)
+
+Signed-off-by: Ryan McCabe <rmccabe@redhat.com>
+Resolves: rhbz#bz1497954
+---
+ cloudinit/net/eni.py        | 33 ++++++++++++++++++--------------
+ tests/unittests/test_net.py | 46 +++++++++++++++++++++++++++++++++------------
+ 2 files changed, 53 insertions(+), 26 deletions(-)
+
+diff --git a/cloudinit/net/eni.py b/cloudinit/net/eni.py
+index b06ffac9..5b249f1f 100644
+--- a/cloudinit/net/eni.py
++++ b/cloudinit/net/eni.py
+@@ -90,8 +90,6 @@ def _iface_add_attrs(iface, index):
+ 
+ def _iface_start_entry(iface, index, render_hwaddress=False):
+     fullname = iface['name']
+-    if index != 0:
+-        fullname += ":%s" % index
+ 
+     control = iface['control']
+     if control == "auto":
+@@ -113,6 +111,16 @@ def _iface_start_entry(iface, index, render_hwaddress=False):
+     return lines
+ 
+ 
++def _subnet_is_ipv6(subnet):
++    # 'static6' or 'dhcp6'
++    if subnet['type'].endswith('6'):
++        # This is a request for DHCPv6.
++        return True
++    elif subnet['type'] == 'static' and ":" in subnet['address']:
++        return True
++    return False
++
++
+ def _parse_deb_config_data(ifaces, contents, src_dir, src_path):
+     """Parses the file contents, placing result into ifaces.
+ 
+@@ -354,21 +362,23 @@ class Renderer(renderer.Renderer):
+         sections = []
+         subnets = iface.get('subnets', {})
+         if subnets:
+-            for index, subnet in zip(range(0, len(subnets)), subnets):
++            for index, subnet in enumerate(subnets):
+                 iface['index'] = index
+                 iface['mode'] = subnet['type']
+                 iface['control'] = subnet.get('control', 'auto')
+                 subnet_inet = 'inet'
+-                if iface['mode'].endswith('6'):
+-                    # This is a request for DHCPv6.
+-                    subnet_inet += '6'
+-                elif iface['mode'] == 'static' and ":" in subnet['address']:
+-                    # This is a static IPv6 address.
++                if _subnet_is_ipv6(subnet):
+                     subnet_inet += '6'
+                 iface['inet'] = subnet_inet
+-                if iface['mode'].startswith('dhcp'):
++                if subnet['type'].startswith('dhcp'):
+                     iface['mode'] = 'dhcp'
+ 
++                # do not emit multiple 'auto $IFACE' lines as older (precise)
++                # ifupdown complains
++                if True in ["auto %s" % (iface['name']) in line
++                            for line in sections]:
++                    iface['control'] = 'alias'
++
+                 lines = list(
+                     _iface_start_entry(
+                         iface, index, render_hwaddress=render_hwaddress) +
+@@ -378,11 +388,6 @@ class Renderer(renderer.Renderer):
+                 for route in subnet.get('routes', []):
+                     lines.extend(self._render_route(route, indent="    "))
+ 
+-                if len(subnets) > 1 and index == 0:
+-                    tmpl = "    post-up ifup %s:%s\n"
+-                    for i in range(1, len(subnets)):
+-                        lines.append(tmpl % (iface['name'], i))
+-
+                 sections.append(lines)
+         else:
+             # ifenslave docs say to auto the slave devices
+diff --git a/tests/unittests/test_net.py b/tests/unittests/test_net.py
+index ffa911cc..4c0e3ad3 100644
+--- a/tests/unittests/test_net.py
++++ b/tests/unittests/test_net.py
+@@ -376,11 +376,9 @@ NETWORK_CONFIGS = {
+ 
+             auto eth99
+             iface eth99 inet dhcp
+-                post-up ifup eth99:1
+ 
+-
+-            auto eth99:1
+-            iface eth99:1 inet static
++            # control-alias eth99
++            iface eth99 inet static
+                 address 192.168.21.3/24
+                 dns-nameservers 8.8.8.8 8.8.4.4
+                 dns-search barley.maas sach.maas
+@@ -418,6 +416,27 @@ NETWORK_CONFIGS = {
+                     - wark.maas
+         """),
+     },
++    'v4_and_v6': {
++        'expected_eni': textwrap.dedent("""\
++            auto lo
++            iface lo inet loopback
++
++            auto iface0
++            iface iface0 inet dhcp
++
++            # control-alias iface0
++            iface iface0 inet6 dhcp
++        """).rstrip(' '),
++        'yaml': textwrap.dedent("""\
++            version: 1
++            config:
++              - type: 'physical'
++                name: 'iface0'
++                subnets:
++                - {'type': 'dhcp4'}
++                - {'type': 'dhcp6'}
++        """).rstrip(' '),
++    },
+     'all': {
+         'expected_eni': ("""\
+ auto lo
+@@ -455,11 +474,9 @@ iface br0 inet static
+     address 192.168.14.2/24
+     bridge_ports eth3 eth4
+     bridge_stp off
+-    post-up ifup br0:1
+-
+ 
+-auto br0:1
+-iface br0:1 inet6 static
++# control-alias br0
++iface br0 inet6 static
+     address 2001:1::1/64
+ 
+ auto bond0.200
+@@ -476,11 +493,9 @@ iface eth0.101 inet static
+     mtu 1500
+     vlan-raw-device eth0
+     vlan_id 101
+-    post-up ifup eth0.101:1
+-
+ 
+-auto eth0.101:1
+-iface eth0.101:1 inet static
++# control-alias eth0.101
++iface eth0.101 inet static
+     address 192.168.2.10/24
+ 
+ post-up route add -net 10.0.0.0 netmask 255.0.0.0 gw 11.0.0.1 metric 3 || true
+@@ -1007,6 +1022,13 @@ class TestEniRoundTrip(TestCase):
+             entry['expected_eni'].splitlines(),
+             files['/etc/network/interfaces'].splitlines())
+ 
++    def testsimple_render_v4_and_v6(self):
++        entry = NETWORK_CONFIGS['v4_and_v6']
++        files = self._render_and_read(network_config=yaml.load(entry['yaml']))
++        self.assertEqual(
++            entry['expected_eni'].splitlines(),
++            files['/etc/network/interfaces'].splitlines())
++
+     def test_routes_rendered(self):
+         # as reported in bug 1649652
+         conf = [
+-- 
+2.13.6
+
diff --git a/SOURCES/0027-systemd-create-run-cloud-init-enabled.patch b/SOURCES/0027-systemd-create-run-cloud-init-enabled.patch
deleted file mode 100644
index 7b822e6..0000000
--- a/SOURCES/0027-systemd-create-run-cloud-init-enabled.patch
+++ /dev/null
@@ -1,29 +0,0 @@
-From e210660ecaee3b44a6e8b4e0fe39e4055450696e Mon Sep 17 00:00:00 2001
-From: Ryan McCabe <rmccabe@redhat.com>
-Date: Fri, 10 Nov 2017 10:03:44 -0500
-Subject: [PATCH] Create an explicit enabled file in /run/cloud-init/ to
- control whether the dhclient and NM hooks run on Azure.
-
-X-downstream-only: Yes
-Resolves: rhbz#1474226
----
- rhel/systemd/cloud-init-local.service | 3 +++
- 1 file changed, 3 insertions(+)
-
-diff --git a/rhel/systemd/cloud-init-local.service b/rhel/systemd/cloud-init-local.service
-index 8174937b..047907c4 100644
---- a/rhel/systemd/cloud-init-local.service
-+++ b/rhel/systemd/cloud-init-local.service
-@@ -14,6 +14,9 @@ ConditionKernelCommandLine=!cloud-init=disabled
- 
- [Service]
- Type=oneshot
-+ExecStartPre=/bin/mkdir -p /run/cloud-init
-+ExecStartPre=/sbin/restorecon /run/cloud-init
-+ExecStartPre=/usr/bin/touch /run/cloud-init/enabled
- ExecStart=/usr/bin/cloud-init init --local
- ExecStart=/bin/touch /run/cloud-init/network-config-ready
- RemainAfterExit=yes
--- 
-2.13.6
-
diff --git a/SOURCES/0028-net-Allow-for-NetworkManager-configuration.patch b/SOURCES/0028-net-Allow-for-NetworkManager-configuration.patch
deleted file mode 100644
index 72f6157..0000000
--- a/SOURCES/0028-net-Allow-for-NetworkManager-configuration.patch
+++ /dev/null
@@ -1,164 +0,0 @@
-From 5fc5da29e5187ff6f56c968e7c06fabd1fce62ad Mon Sep 17 00:00:00 2001
-From: Ryan McCabe <rmccabe@redhat.com>
-Date: Thu, 8 Jun 2017 13:24:23 -0400
-Subject: [PATCH] net: Allow for NetworkManager configuration
-
-In cases where the config json specifies nameserver entries,
-if there are interfaces configured to use dhcp, NetworkManager,
-if enabled, will clobber the /etc/resolv.conf that cloud-init
-has produced, which can break dns. If there are no interfaces
-configured to use dhcp, NetworkManager could clobber
-/etc/resolv.conf with an empty file.
-
-This patch adds a mechanism for dropping additional configuration
-into /etc/NetworkManager/conf.d/ and disables management of
-/etc/resolv.conf by NetworkManager when nameserver information is
-provided in the config.
-
-LP: #1693251
-
-Resolves: rhbz#1454491
-
-Signed-off-by: Ryan McCabe <rmccabe@redhat.com>
-(cherry picked from commit 67bab5bb804e2346673430868935f6bbcdb88f13)
----
- cloudinit/distros/parsers/networkmanager_conf.py | 23 +++++++++++++++++++++++
- cloudinit/net/sysconfig.py                       | 24 ++++++++++++++++++++++++
- tests/unittests/test_net.py                      | 21 +++++++++++++++++++++
- 3 files changed, 68 insertions(+)
- create mode 100644 cloudinit/distros/parsers/networkmanager_conf.py
-
-diff --git a/cloudinit/distros/parsers/networkmanager_conf.py b/cloudinit/distros/parsers/networkmanager_conf.py
-new file mode 100644
-index 00000000..ac51f122
---- /dev/null
-+++ b/cloudinit/distros/parsers/networkmanager_conf.py
-@@ -0,0 +1,23 @@
-+# Copyright (C) 2017 Red Hat, Inc.
-+#
-+# Author: Ryan McCabe <rmccabe@redhat.com>
-+#
-+# This file is part of cloud-init. See LICENSE file for license information.
-+
-+import configobj
-+
-+# This module is used to set additional NetworkManager configuration
-+# in /etc/NetworkManager/conf.d
-+#
-+
-+
-+class NetworkManagerConf(configobj.ConfigObj):
-+    def __init__(self, contents):
-+        configobj.ConfigObj.__init__(self, contents,
-+                                     interpolation=False,
-+                                     write_empty_values=False)
-+
-+    def set_section_keypair(self, section_name, key, value):
-+        if section_name not in self.sections:
-+            self.main[section_name] = {}
-+        self.main[section_name] = {key: value}
-diff --git a/cloudinit/net/sysconfig.py b/cloudinit/net/sysconfig.py
-index ef80d99b..d496d916 100644
---- a/cloudinit/net/sysconfig.py
-+++ b/cloudinit/net/sysconfig.py
-@@ -5,6 +5,7 @@ import re
- 
- import six
- 
-+from cloudinit.distros.parsers import networkmanager_conf
- from cloudinit.distros.parsers import resolv_conf
- from cloudinit import util
- 
-@@ -250,6 +251,9 @@ class Renderer(renderer.Renderer):
-         self.netrules_path = config.get(
-             'netrules_path', 'etc/udev/rules.d/70-persistent-net.rules')
-         self.dns_path = config.get('dns_path', 'etc/resolv.conf')
-+        nm_conf_path = 'etc/NetworkManager/conf.d/99-cloud-init.conf'
-+        self.networkmanager_conf_path = config.get('networkmanager_conf_path',
-+                                                   nm_conf_path)
- 
-     @classmethod
-     def _render_iface_shared(cls, iface, iface_cfg):
-@@ -443,6 +447,21 @@ class Renderer(renderer.Renderer):
-             content.add_search_domain(searchdomain)
-         return "\n".join([_make_header(';'), str(content)])
- 
-+    @staticmethod
-+    def _render_networkmanager_conf(network_state):
-+        content = networkmanager_conf.NetworkManagerConf("")
-+
-+        # If DNS server information is provided, configure
-+        # NetworkManager to not manage dns, so that /etc/resolv.conf
-+        # does not get clobbered.
-+        if network_state.dns_nameservers:
-+            content.set_section_keypair('main', 'dns', 'none')
-+
-+        if len(content) == 0:
-+            return None
-+        out = "".join([_make_header(), "\n", "\n".join(content.write()), "\n"])
-+        return out
-+
-     @classmethod
-     def _render_bridge_interfaces(cls, network_state, iface_contents):
-         bridge_filter = renderer.filter_by_type('bridge')
-@@ -500,6 +519,11 @@ class Renderer(renderer.Renderer):
-             resolv_content = self._render_dns(network_state,
-                                               existing_dns_path=dns_path)
-             util.write_file(dns_path, resolv_content)
-+        if self.networkmanager_conf_path:
-+            nm_conf_path = os.path.join(target, self.networkmanager_conf_path)
-+            nm_conf_content = self._render_networkmanager_conf(network_state)
-+            if nm_conf_content:
-+                util.write_file(nm_conf_path, nm_conf_content)
-         if self.netrules_path:
-             netrules_content = self._render_persistent_net(network_state)
-             netrules_path = os.path.join(target, self.netrules_path)
-diff --git a/tests/unittests/test_net.py b/tests/unittests/test_net.py
-index 172d6046..379ac8bb 100755
---- a/tests/unittests/test_net.py
-+++ b/tests/unittests/test_net.py
-@@ -162,6 +162,13 @@ NETMASK0=0.0.0.0
- ;
- nameserver 172.19.0.12
- """.lstrip()),
-+            ('etc/NetworkManager/conf.d/99-cloud-init.conf',
-+             """
-+# Created by cloud-init on instance boot automatically, do not edit.
-+#
-+[main]
-+dns = none
-+""".lstrip()),
-             ('etc/udev/rules.d/70-persistent-net.rules',
-              "".join(['SUBSYSTEM=="net", ACTION=="add", DRIVERS=="?*", ',
-                       'ATTR{address}=="fa:16:3e:ed:9a:59", NAME="eth0"\n']))]
-@@ -222,6 +229,13 @@ USERCTL=no
- ;
- nameserver 172.19.0.12
- """.lstrip()),
-+            ('etc/NetworkManager/conf.d/99-cloud-init.conf',
-+             """
-+# Created by cloud-init on instance boot automatically, do not edit.
-+#
-+[main]
-+dns = none
-+""".lstrip()),
-             ('etc/udev/rules.d/70-persistent-net.rules',
-              "".join(['SUBSYSTEM=="net", ACTION=="add", DRIVERS=="?*", ',
-                       'ATTR{address}=="fa:16:3e:ed:9a:59", NAME="eth0"\n']))]
-@@ -304,6 +318,13 @@ USERCTL=no
- ;
- nameserver 172.19.0.12
- """.lstrip()),
-+            ('etc/NetworkManager/conf.d/99-cloud-init.conf',
-+             """
-+# Created by cloud-init on instance boot automatically, do not edit.
-+#
-+[main]
-+dns = none
-+""".lstrip()),
-             ('etc/udev/rules.d/70-persistent-net.rules',
-              "".join(['SUBSYSTEM=="net", ACTION=="add", DRIVERS=="?*", ',
-                       'ATTR{address}=="fa:16:3e:ed:9a:59", NAME="eth0"\n']))]
--- 
-2.13.6
-
diff --git a/SOURCES/0028-systemd-create-run-cloud-init-enabled.patch b/SOURCES/0028-systemd-create-run-cloud-init-enabled.patch
new file mode 100644
index 0000000..7b822e6
--- /dev/null
+++ b/SOURCES/0028-systemd-create-run-cloud-init-enabled.patch
@@ -0,0 +1,29 @@
+From e210660ecaee3b44a6e8b4e0fe39e4055450696e Mon Sep 17 00:00:00 2001
+From: Ryan McCabe <rmccabe@redhat.com>
+Date: Fri, 10 Nov 2017 10:03:44 -0500
+Subject: [PATCH] Create an explicit enabled file in /run/cloud-init/ to
+ control whether the dhclient and NM hooks run on Azure.
+
+X-downstream-only: Yes
+Resolves: rhbz#1474226
+---
+ rhel/systemd/cloud-init-local.service | 3 +++
+ 1 file changed, 3 insertions(+)
+
+diff --git a/rhel/systemd/cloud-init-local.service b/rhel/systemd/cloud-init-local.service
+index 8174937b..047907c4 100644
+--- a/rhel/systemd/cloud-init-local.service
++++ b/rhel/systemd/cloud-init-local.service
+@@ -14,6 +14,9 @@ ConditionKernelCommandLine=!cloud-init=disabled
+ 
+ [Service]
+ Type=oneshot
++ExecStartPre=/bin/mkdir -p /run/cloud-init
++ExecStartPre=/sbin/restorecon /run/cloud-init
++ExecStartPre=/usr/bin/touch /run/cloud-init/enabled
+ ExecStart=/usr/bin/cloud-init init --local
+ ExecStart=/bin/touch /run/cloud-init/network-config-ready
+ RemainAfterExit=yes
+-- 
+2.13.6
+
diff --git a/SOURCES/0029-support-loopback-as-a-device-type.patch b/SOURCES/0029-support-loopback-as-a-device-type.patch
index d421ad5..285efc4 100644
--- a/SOURCES/0029-support-loopback-as-a-device-type.patch
+++ b/SOURCES/0029-support-loopback-as-a-device-type.patch
@@ -1,7 +1,7 @@
-From 4aa2305022e5d42d858102941c51d7186ef8fef9 Mon Sep 17 00:00:00 2001
+From 8e8e1dffc528b738f92cd508d7a6faf58e40af00 Mon Sep 17 00:00:00 2001
 From: Scott Moser <smoser@brickies.net>
 Date: Wed, 15 Mar 2017 12:06:40 -0400
-Subject: [PATCH 1/3] support 'loopback' as a device type.
+Subject: [PATCH 1/2] support 'loopback' as a device type.
 
 As reported in bug 1671927, sysconfig had an issue with rendering
 a loopback device.  The problem was that some as yet unknown issue was
@@ -21,7 +21,8 @@ Tests are added for eni and sysconfig renderer.
 
 (cherry picked from commit 1a2ca7530518d819cbab7287b12f942743427e38)
 
-Related: rhbz#1540094
+Related: rhbz#1492726
+Signed-off-by: Ryan McCabe <rmccabe@redhat.com>
 ---
  cloudinit/net/eni.py           | 16 +++++++++------
  cloudinit/net/network_state.py |  4 ++++
@@ -30,10 +31,10 @@ Related: rhbz#1540094
  4 files changed, 60 insertions(+), 6 deletions(-)
 
 diff --git a/cloudinit/net/eni.py b/cloudinit/net/eni.py
-index b06ffac9..0d5e9712 100644
+index 5b249f1f..69ecbb5d 100644
 --- a/cloudinit/net/eni.py
 +++ b/cloudinit/net/eni.py
-@@ -265,8 +265,11 @@ def _ifaces_to_net_config_data(ifaces):
+@@ -273,8 +273,11 @@ def _ifaces_to_net_config_data(ifaces):
          # devname is 'eth0' for name='eth0:1'
          devname = name.partition(":")[0]
          if devname not in devs:
@@ -47,7 +48,7 @@ index b06ffac9..0d5e9712 100644
              # this isnt strictly correct, but some might specify
              # hwaddress on a nic for matching / declaring name.
              if 'hwaddress' in data:
-@@ -418,10 +421,11 @@ class Renderer(renderer.Renderer):
+@@ -423,10 +426,11 @@ class Renderer(renderer.Renderer):
              bonding
          '''
          order = {
@@ -67,17 +68,17 @@ diff --git a/cloudinit/net/network_state.py b/cloudinit/net/network_state.py
 index 11ef585b..90b2835a 100644
 --- a/cloudinit/net/network_state.py
 +++ b/cloudinit/net/network_state.py
-@@ -211,6 +211,10 @@ class NetworkStateInterpreter(object):
-                              exc_info=True)
+@@ -212,6 +212,10 @@ class NetworkStateInterpreter(object):
                      LOG.debug(self.dump_network_state())
  
-+    @ensure_command_keys(['name'])
+     @ensure_command_keys(['name'])
 +    def handle_loopback(self, command):
 +        return self.handle_physical(command)
 +
-     @ensure_command_keys(['name'])
++    @ensure_command_keys(['name'])
      def handle_physical(self, command):
          '''
+         command = {
 diff --git a/cloudinit/net/sysconfig.py b/cloudinit/net/sysconfig.py
 index efd101ca..25c29104 100644
 --- a/cloudinit/net/sysconfig.py
@@ -92,10 +93,10 @@ index efd101ca..25c29104 100644
              iface_cfg = NetInterface(iface_name, base_sysconf_dir)
              cls._render_iface_shared(iface, iface_cfg)
 diff --git a/tests/unittests/test_net.py b/tests/unittests/test_net.py
-index ffa911cc..d75742be 100644
+index 4c0e3ad3..7e389c10 100644
 --- a/tests/unittests/test_net.py
 +++ b/tests/unittests/test_net.py
-@@ -600,6 +600,14 @@ pre-down route del -net 10.0.0.0 netmask 255.0.0.0 gw 11.0.0.1 metric 3 || true
+@@ -615,6 +615,14 @@ pre-down route del -net 10.0.0.0 netmask 255.0.0.0 gw 11.0.0.1 metric 3 || true
      }
  }
  
@@ -110,7 +111,7 @@ index ffa911cc..d75742be 100644
  
  def _setup_test(tmp_dir, mock_get_devicelist, mock_read_sys_net,
                  mock_sys_dev_path):
-@@ -770,6 +778,27 @@ USERCTL=no
+@@ -785,6 +793,27 @@ USERCTL=no
                  with open(os.path.join(render_dir, fn)) as fh:
                      self.assertEqual(expected_content, fh.read())
  
@@ -138,7 +139,7 @@ index ffa911cc..d75742be 100644
  
  class TestEniNetRendering(TestCase):
  
-@@ -811,6 +840,21 @@ iface eth1000 inet dhcp
+@@ -826,6 +855,21 @@ iface eth1000 inet dhcp
  """
          self.assertEqual(expected.lstrip(), contents.lstrip())
  
@@ -161,5 +162,5 @@ index ffa911cc..d75742be 100644
  class TestEniNetworkStateToEni(TestCase):
      mycfg = {
 -- 
-2.14.3
+2.13.6
 
diff --git a/SOURCES/0030-Render-the-GATEWAY-value-in-interface-files-which-ha.patch b/SOURCES/0030-Render-the-GATEWAY-value-in-interface-files-which-ha.patch
deleted file mode 100644
index c05332b..0000000
--- a/SOURCES/0030-Render-the-GATEWAY-value-in-interface-files-which-ha.patch
+++ /dev/null
@@ -1,29 +0,0 @@
-From 6f9c05464a8ed420fb3e2ed71b401ecd1d772bad Mon Sep 17 00:00:00 2001
-From: Ryan McCabe <rmccabe@redhat.com>
-Date: Tue, 30 Jan 2018 12:37:00 -0500
-Subject: [PATCH 2/3] Render the GATEWAY= value in interface files which have a
- gateway in the subnet configuration.
-
-Resolves: rhbz#1540094
-Signed-off-by: Ryan McCabe <rmccabe@redhat.com>
----
- cloudinit/net/sysconfig.py | 3 +++
- 1 file changed, 3 insertions(+)
-
-diff --git a/cloudinit/net/sysconfig.py b/cloudinit/net/sysconfig.py
-index 25c29104..ca031691 100644
---- a/cloudinit/net/sysconfig.py
-+++ b/cloudinit/net/sysconfig.py
-@@ -347,6 +347,9 @@ class Renderer(renderer.Renderer):
-                             iface_cfg['NETMASK' + str(ipv4_index)] = \
-                                 subnet['netmask']
- 
-+                if 'gateway' in subnet:
-+                    iface_cfg['GATEWAY'] = subnet['gateway']
-+
-     @classmethod
-     def _render_subnet_routes(cls, iface_cfg, route_cfg, subnets):
-         for i, subnet in enumerate(subnets, start=len(iface_cfg.children)):
--- 
-2.14.3
-
diff --git a/SOURCES/0030-sysconfig-include-GATEWAY-value-if-set-in-subnet.patch b/SOURCES/0030-sysconfig-include-GATEWAY-value-if-set-in-subnet.patch
new file mode 100644
index 0000000..8b911fd
--- /dev/null
+++ b/SOURCES/0030-sysconfig-include-GATEWAY-value-if-set-in-subnet.patch
@@ -0,0 +1,142 @@
+From 8fd2f03c40606ad25135385f7badf42e48d713b6 Mon Sep 17 00:00:00 2001
+From: Ryan Harper <ryan.harper@canonical.com>
+Date: Fri, 9 Jun 2017 12:35:11 -0500
+Subject: [PATCH 2/2] sysconfig: include GATEWAY value if set in subnet
+
+Render the GATEWAY= value in interface files which have a gateway in the
+subnet configuration.
+
+LP: #1686856
+(cherry picked from commit d1e8eb73aca6a3f5cee415774dcf540e934ec250)
+
+Resolves: rhbz#1465730
+Signed-off-by: Ryan McCabe <rmccabe@redhat.com>
+(cherry picked from commit 158f53f223b763ddfbfa5967c58866424ae02689)
+
+Resolves: rhbz#1492726
+Signed-off-by: Ryan McCabe <rmccabe@redhat.com>
+---
+ cloudinit/net/sysconfig.py                     |  3 ++
+ tests/unittests/test_distros/test_netconfig.py |  2 ++
+ tests/unittests/test_net.py                    | 50 +++++++++++++++++++++++++-
+ 3 files changed, 54 insertions(+), 1 deletion(-)
+
+diff --git a/cloudinit/net/sysconfig.py b/cloudinit/net/sysconfig.py
+index 25c29104..ca031691 100644
+--- a/cloudinit/net/sysconfig.py
++++ b/cloudinit/net/sysconfig.py
+@@ -347,6 +347,9 @@ class Renderer(renderer.Renderer):
+                             iface_cfg['NETMASK' + str(ipv4_index)] = \
+                                 subnet['netmask']
+ 
++                if 'gateway' in subnet:
++                    iface_cfg['GATEWAY'] = subnet['gateway']
++
+     @classmethod
+     def _render_subnet_routes(cls, iface_cfg, route_cfg, subnets):
+         for i, subnet in enumerate(subnets, start=len(iface_cfg.children)):
+diff --git a/tests/unittests/test_distros/test_netconfig.py b/tests/unittests/test_distros/test_netconfig.py
+index 861cf8ef..108d5741 100644
+--- a/tests/unittests/test_distros/test_netconfig.py
++++ b/tests/unittests/test_distros/test_netconfig.py
+@@ -260,6 +260,7 @@ BOOTPROTO=none
+ DEVICE=eth0
+ IPADDR=192.168.1.5
+ NETMASK=255.255.255.0
++GATEWAY=192.168.1.254
+ ONBOOT=yes
+ TYPE=Ethernet
+ USERCTL=no
+@@ -396,6 +397,7 @@ IPV6_AUTOCONF=no
+ BOOTPROTO=none
+ DEVICE=eth0
+ IPV6ADDR=2607:f0d0:1002:0011::2/64
++GATEWAY=2607:f0d0:1002:0011::1
+ IPV6INIT=yes
+ ONBOOT=yes
+ TYPE=Ethernet
+diff --git a/tests/unittests/test_net.py b/tests/unittests/test_net.py
+index 7e389c10..4a32eb88 100644
+--- a/tests/unittests/test_net.py
++++ b/tests/unittests/test_net.py
+@@ -615,6 +615,7 @@ pre-down route del -net 10.0.0.0 netmask 255.0.0.0 gw 11.0.0.1 metric 3 || true
+     }
+ }
+ 
++
+ CONFIG_V1_EXPLICIT_LOOPBACK = {
+     'version': 1,
+     'config': [{'name': 'eth0', 'type': 'physical',
+@@ -624,6 +625,30 @@ CONFIG_V1_EXPLICIT_LOOPBACK = {
+                ]}
+ 
+ 
++CONFIG_V1_SIMPLE_SUBNET = {
++    'version': 1,
++    'config': [{'mac_address': '52:54:00:12:34:00',
++                'name': 'interface0',
++                'subnets': [{'address': '10.0.2.15',
++                             'gateway': '10.0.2.2',
++                             'netmask': '255.255.255.0',
++                             'type': 'static'}],
++                'type': 'physical'}]}
++
++
++DEFAULT_DEV_ATTRS = {
++    'eth1000': {
++        "bridge": False,
++        "carrier": False,
++        "dormant": False,
++        "operstate": "down",
++        "address": "07-1C-C6-75-A4-BE",
++        "device/driver": None,
++        "device/device": None,
++    }
++}
++
++
+ def _setup_test(tmp_dir, mock_get_devicelist, mock_read_sys_net,
+                 mock_sys_dev_path):
+     mock_get_devicelist.return_value = ['eth1000']
+@@ -793,6 +818,30 @@ USERCTL=no
+                 with open(os.path.join(render_dir, fn)) as fh:
+                     self.assertEqual(expected_content, fh.read())
+ 
++    def test_network_config_v1_samples(self):
++        ns = network_state.parse_net_config_data(CONFIG_V1_SIMPLE_SUBNET)
++        render_dir = self.tmp_path("render")
++        os.makedirs(render_dir)
++        renderer = sysconfig.Renderer()
++        renderer.render_network_state(render_dir, ns)
++        found = dir2dict(render_dir)
++        nspath = '/etc/sysconfig/network-scripts/'
++        self.assertNotIn(nspath + 'ifcfg-lo', found.keys())
++        expected = """\
++# Created by cloud-init on instance boot automatically, do not edit.
++#
++BOOTPROTO=none
++DEVICE=interface0
++GATEWAY=10.0.2.2
++HWADDR=52:54:00:12:34:00
++IPADDR=10.0.2.15
++NETMASK=255.255.255.0
++ONBOOT=yes
++TYPE=Ethernet
++USERCTL=no
++"""
++        self.assertEqual(expected, found[nspath + 'ifcfg-interface0'])
++
+     def test_config_with_explicit_loopback(self):
+         ns = network_state.parse_net_config_data(CONFIG_V1_EXPLICIT_LOOPBACK)
+         render_dir = self.tmp_path("render")
+@@ -807,7 +856,6 @@ USERCTL=no
+ #
+ BOOTPROTO=dhcp
+ DEVICE=eth0
+-NM_CONTROLLED=no
+ ONBOOT=yes
+ TYPE=Ethernet
+ USERCTL=no
+-- 
+2.13.6
+
diff --git a/SOURCES/0031-rh_subscription-Perform-null-checks-for-enabled-and-.patch b/SOURCES/0031-rh_subscription-Perform-null-checks-for-enabled-and-.patch
new file mode 100644
index 0000000..4ded147
--- /dev/null
+++ b/SOURCES/0031-rh_subscription-Perform-null-checks-for-enabled-and-.patch
@@ -0,0 +1,190 @@
+From fc4ca923d02886669f2f0e0916732133e1969bb6 Mon Sep 17 00:00:00 2001
+From: Dave Mulford <dmulford@redhat.com>
+Date: Mon, 9 Oct 2017 15:28:15 -0500
+Subject: [PATCH] rh_subscription: Perform null checks for enabled and disabled
+ repos.
+
+The rh_subscription module doesn't perform null checks when attempting to
+iterate on the enabled and disable repos arrays. When only one is
+specified, cloud-init fails to run.
+
+(cherry picked from commit 9bc4ce0596544ffa56d9d67245b00e07006a8662)
+
+Resolves: rhbz#1498974
+Signed-off-by: Ryan McCabe <rmccabe@redhat.com>
+---
+ cloudinit/config/cc_rh_subscription.py  | 46 ++++++++++++++++++++-------------
+ tests/unittests/test_rh_subscription.py | 15 +++++++++++
+ 2 files changed, 43 insertions(+), 18 deletions(-)
+
+diff --git a/cloudinit/config/cc_rh_subscription.py b/cloudinit/config/cc_rh_subscription.py
+index 7f36cf8f..a9d21e78 100644
+--- a/cloudinit/config/cc_rh_subscription.py
++++ b/cloudinit/config/cc_rh_subscription.py
+@@ -38,14 +38,16 @@ Subscription`` example config.
+         server-hostname: <hostname>
+ """
+ 
++from cloudinit import log as logging
+ from cloudinit import util
+ 
++LOG = logging.getLogger(__name__)
++
+ distros = ['fedora', 'rhel']
+ 
+ 
+ def handle(name, cfg, _cloud, log, _args):
+-    sm = SubscriptionManager(cfg)
+-    sm.log = log
++    sm = SubscriptionManager(cfg, log=log)
+     if not sm.is_configured():
+         log.debug("%s: module not configured.", name)
+         return None
+@@ -86,10 +88,9 @@ def handle(name, cfg, _cloud, log, _args):
+                 if not return_stat:
+                     raise SubscriptionError("Unable to attach pools {0}"
+                                             .format(sm.pools))
+-            if (sm.enable_repo is not None) or (sm.disable_repo is not None):
+-                return_stat = sm.update_repos(sm.enable_repo, sm.disable_repo)
+-                if not return_stat:
+-                    raise SubscriptionError("Unable to add or remove repos")
++            return_stat = sm.update_repos()
++            if not return_stat:
++                raise SubscriptionError("Unable to add or remove repos")
+             sm.log_success("rh_subscription plugin completed successfully")
+         except SubscriptionError as e:
+             sm.log_warn(str(e))
+@@ -108,7 +109,10 @@ class SubscriptionManager(object):
+                      'rhsm-baseurl', 'server-hostname',
+                      'auto-attach', 'service-level']
+ 
+-    def __init__(self, cfg):
++    def __init__(self, cfg, log=None):
++        if log is None:
++            log = LOG
++        self.log = log
+         self.cfg = cfg
+         self.rhel_cfg = self.cfg.get('rh_subscription', {})
+         self.rhsm_baseurl = self.rhel_cfg.get('rhsm-baseurl')
+@@ -130,7 +134,7 @@ class SubscriptionManager(object):
+ 
+     def log_warn(self, msg):
+         '''Simple wrapper for logging warning messages. Useful for unittests'''
+-        self.log.warn(msg)
++        self.log.warning(msg)
+ 
+     def _verify_keys(self):
+         '''
+@@ -245,7 +249,7 @@ class SubscriptionManager(object):
+             return False
+ 
+         reg_id = return_out.split("ID: ")[1].rstrip()
+-        self.log.debug("Registered successfully with ID {0}".format(reg_id))
++        self.log.debug("Registered successfully with ID %s", reg_id)
+         return True
+ 
+     def _set_service_level(self):
+@@ -347,7 +351,7 @@ class SubscriptionManager(object):
+             try:
+                 self._sub_man_cli(cmd)
+                 self.log.debug("Attached the following pools to your "
+-                               "system: %s" % (", ".join(pool_list))
++                               "system: %s", (", ".join(pool_list))
+                                .replace('--pool=', ''))
+                 return True
+             except util.ProcessExecutionError as e:
+@@ -355,18 +359,24 @@ class SubscriptionManager(object):
+                               "due to {1}".format(pool, e))
+                 return False
+ 
+-    def update_repos(self, erepos, drepos):
++    def update_repos(self):
+         '''
+         Takes a list of yum repo ids that need to be disabled or enabled; then
+         it verifies if they are already enabled or disabled and finally
+         executes the action to disable or enable
+         '''
+ 
+-        if (erepos is not None) and (not isinstance(erepos, list)):
++        erepos = self.enable_repo
++        drepos = self.disable_repo
++        if erepos is None:
++            erepos = []
++        if drepos is None:
++            drepos = []
++        if not isinstance(erepos, list):
+             self.log_warn("Repo IDs must in the format of a list.")
+             return False
+ 
+-        if (drepos is not None) and (not isinstance(drepos, list)):
++        if not isinstance(drepos, list):
+             self.log_warn("Repo IDs must in the format of a list.")
+             return False
+ 
+@@ -399,14 +409,14 @@ class SubscriptionManager(object):
+             for fail in enable_list_fail:
+                 # Check if the repo exists or not
+                 if fail in active_repos:
+-                    self.log.debug("Repo {0} is already enabled".format(fail))
++                    self.log.debug("Repo %s is already enabled", fail)
+                 else:
+                     self.log_warn("Repo {0} does not appear to "
+                                   "exist".format(fail))
+         if len(disable_list_fail) > 0:
+             for fail in disable_list_fail:
+-                self.log.debug("Repo {0} not disabled "
+-                               "because it is not enabled".format(fail))
++                self.log.debug("Repo %s not disabled "
++                               "because it is not enabled", fail)
+ 
+         cmd = ['repos']
+         if len(disable_list) > 0:
+@@ -422,10 +432,10 @@ class SubscriptionManager(object):
+             return False
+ 
+         if len(enable_list) > 0:
+-            self.log.debug("Enabled the following repos: %s" %
++            self.log.debug("Enabled the following repos: %s",
+                            (", ".join(enable_list)).replace('--enable=', ''))
+         if len(disable_list) > 0:
+-            self.log.debug("Disabled the following repos: %s" %
++            self.log.debug("Disabled the following repos: %s",
+                            (", ".join(disable_list)).replace('--disable=', ''))
+         return True
+ 
+diff --git a/tests/unittests/test_rh_subscription.py b/tests/unittests/test_rh_subscription.py
+index ca14cd46..7b35b9d0 100644
+--- a/tests/unittests/test_rh_subscription.py
++++ b/tests/unittests/test_rh_subscription.py
+@@ -2,6 +2,7 @@
+ 
+ """Tests for registering RHEL subscription via rh_subscription."""
+ 
++import copy
+ import logging
+ 
+ from cloudinit.config import cc_rh_subscription
+@@ -68,6 +69,20 @@ class GoodTests(TestCase):
+         self.assertEqual(self.SM.log_success.call_count, 1)
+         self.assertEqual(self.SM._sub_man_cli.call_count, 2)
+ 
++    @mock.patch.object(cc_rh_subscription.SubscriptionManager, "_getRepos")
++    @mock.patch.object(cc_rh_subscription.SubscriptionManager, "_sub_man_cli")
++    def test_update_repos_disable_with_none(self, m_sub_man_cli, m_get_repos):
++        cfg = copy.deepcopy(self.config)
++        m_get_repos.return_value = ([], ['repo1'])
++        m_sub_man_cli.return_value = (b'', b'')
++        cfg['rh_subscription'].update(
++            {'enable-repo': ['repo1'], 'disable-repo': None})
++        mysm = cc_rh_subscription.SubscriptionManager(cfg)
++        self.assertEqual(True, mysm.update_repos())
++        m_get_repos.assert_called_with()
++        self.assertEqual(m_sub_man_cli.call_args_list,
++                         [mock.call(['repos', '--enable=repo1'])])
++
+     def test_full_registration(self):
+         '''
+         Registration with auto-attach, service-level, adding pools,
+-- 
+2.13.6
+
diff --git a/SOURCES/0031-sysconfig-Don-t-write-BOOTPROTO-dhcp-for-ipv6-dhcp.patch b/SOURCES/0031-sysconfig-Don-t-write-BOOTPROTO-dhcp-for-ipv6-dhcp.patch
deleted file mode 100644
index 57c49ba..0000000
--- a/SOURCES/0031-sysconfig-Don-t-write-BOOTPROTO-dhcp-for-ipv6-dhcp.patch
+++ /dev/null
@@ -1,33 +0,0 @@
-From 92ab19b85a1cdab73280b679c4a3bd0e32f3d2e2 Mon Sep 17 00:00:00 2001
-From: Ryan McCabe <rmccabe@redhat.com>
-Date: Mon, 4 Dec 2017 13:43:30 -0500
-Subject: [PATCH 3/3] sysconfig: Don't write BOOTPROTO=dhcp for ipv6 dhcp
-
-Don't write BOOTPROTO=dhcp for ipv6 dhcp, as BOOTPROTO applies
-only to ipv4. Explicitly write IPV6_AUTOCONF=no for dhcp on ipv6.
-
-X-downstream-only: yes
-
-Resolves: rhbz#1540093
-Signed-off-by: Ryan McCabe <rmccabe@redhat.com>
-(cherry picked from commit cbc09ba2f8e6fa967232039e6f6a3363d54ba592)
----
- cloudinit/net/sysconfig.py | 2 +-
- 1 file changed, 1 insertion(+), 1 deletion(-)
-
-diff --git a/cloudinit/net/sysconfig.py b/cloudinit/net/sysconfig.py
-index ca031691..09df76e3 100644
---- a/cloudinit/net/sysconfig.py
-+++ b/cloudinit/net/sysconfig.py
-@@ -287,7 +287,7 @@ class Renderer(renderer.Renderer):
-             if subnet_type == 'dhcp6':
-                 iface_cfg['IPV6INIT'] = True
-                 iface_cfg['DHCPV6C'] = True
--                iface_cfg['BOOTPROTO'] = 'dhcp'
-+                iface_cfg['IPV6_AUTOCONF'] = False
-             elif subnet_type in ['dhcp4', 'dhcp']:
-                 iface_cfg['BOOTPROTO'] = 'dhcp'
-             elif subnet_type == 'static':
--- 
-2.14.3
-
diff --git a/SOURCES/0032-net-Allow-for-NetworkManager-configuration.patch b/SOURCES/0032-net-Allow-for-NetworkManager-configuration.patch
new file mode 100644
index 0000000..72f6157
--- /dev/null
+++ b/SOURCES/0032-net-Allow-for-NetworkManager-configuration.patch
@@ -0,0 +1,164 @@
+From 5fc5da29e5187ff6f56c968e7c06fabd1fce62ad Mon Sep 17 00:00:00 2001
+From: Ryan McCabe <rmccabe@redhat.com>
+Date: Thu, 8 Jun 2017 13:24:23 -0400
+Subject: [PATCH] net: Allow for NetworkManager configuration
+
+In cases where the config json specifies nameserver entries,
+if there are interfaces configured to use dhcp, NetworkManager,
+if enabled, will clobber the /etc/resolv.conf that cloud-init
+has produced, which can break dns. If there are no interfaces
+configured to use dhcp, NetworkManager could clobber
+/etc/resolv.conf with an empty file.
+
+This patch adds a mechanism for dropping additional configuration
+into /etc/NetworkManager/conf.d/ and disables management of
+/etc/resolv.conf by NetworkManager when nameserver information is
+provided in the config.
+
+LP: #1693251
+
+Resolves: rhbz#1454491
+
+Signed-off-by: Ryan McCabe <rmccabe@redhat.com>
+(cherry picked from commit 67bab5bb804e2346673430868935f6bbcdb88f13)
+---
+ cloudinit/distros/parsers/networkmanager_conf.py | 23 +++++++++++++++++++++++
+ cloudinit/net/sysconfig.py                       | 24 ++++++++++++++++++++++++
+ tests/unittests/test_net.py                      | 21 +++++++++++++++++++++
+ 3 files changed, 68 insertions(+)
+ create mode 100644 cloudinit/distros/parsers/networkmanager_conf.py
+
+diff --git a/cloudinit/distros/parsers/networkmanager_conf.py b/cloudinit/distros/parsers/networkmanager_conf.py
+new file mode 100644
+index 00000000..ac51f122
+--- /dev/null
++++ b/cloudinit/distros/parsers/networkmanager_conf.py
+@@ -0,0 +1,23 @@
++# Copyright (C) 2017 Red Hat, Inc.
++#
++# Author: Ryan McCabe <rmccabe@redhat.com>
++#
++# This file is part of cloud-init. See LICENSE file for license information.
++
++import configobj
++
++# This module is used to set additional NetworkManager configuration
++# in /etc/NetworkManager/conf.d
++#
++
++
++class NetworkManagerConf(configobj.ConfigObj):
++    def __init__(self, contents):
++        configobj.ConfigObj.__init__(self, contents,
++                                     interpolation=False,
++                                     write_empty_values=False)
++
++    def set_section_keypair(self, section_name, key, value):
++        if section_name not in self.sections:
++            self.main[section_name] = {}
++        self.main[section_name] = {key: value}
+diff --git a/cloudinit/net/sysconfig.py b/cloudinit/net/sysconfig.py
+index ef80d99b..d496d916 100644
+--- a/cloudinit/net/sysconfig.py
++++ b/cloudinit/net/sysconfig.py
+@@ -5,6 +5,7 @@ import re
+ 
+ import six
+ 
++from cloudinit.distros.parsers import networkmanager_conf
+ from cloudinit.distros.parsers import resolv_conf
+ from cloudinit import util
+ 
+@@ -250,6 +251,9 @@ class Renderer(renderer.Renderer):
+         self.netrules_path = config.get(
+             'netrules_path', 'etc/udev/rules.d/70-persistent-net.rules')
+         self.dns_path = config.get('dns_path', 'etc/resolv.conf')
++        nm_conf_path = 'etc/NetworkManager/conf.d/99-cloud-init.conf'
++        self.networkmanager_conf_path = config.get('networkmanager_conf_path',
++                                                   nm_conf_path)
+ 
+     @classmethod
+     def _render_iface_shared(cls, iface, iface_cfg):
+@@ -443,6 +447,21 @@ class Renderer(renderer.Renderer):
+             content.add_search_domain(searchdomain)
+         return "\n".join([_make_header(';'), str(content)])
+ 
++    @staticmethod
++    def _render_networkmanager_conf(network_state):
++        content = networkmanager_conf.NetworkManagerConf("")
++
++        # If DNS server information is provided, configure
++        # NetworkManager to not manage dns, so that /etc/resolv.conf
++        # does not get clobbered.
++        if network_state.dns_nameservers:
++            content.set_section_keypair('main', 'dns', 'none')
++
++        if len(content) == 0:
++            return None
++        out = "".join([_make_header(), "\n", "\n".join(content.write()), "\n"])
++        return out
++
+     @classmethod
+     def _render_bridge_interfaces(cls, network_state, iface_contents):
+         bridge_filter = renderer.filter_by_type('bridge')
+@@ -500,6 +519,11 @@ class Renderer(renderer.Renderer):
+             resolv_content = self._render_dns(network_state,
+                                               existing_dns_path=dns_path)
+             util.write_file(dns_path, resolv_content)
++        if self.networkmanager_conf_path:
++            nm_conf_path = os.path.join(target, self.networkmanager_conf_path)
++            nm_conf_content = self._render_networkmanager_conf(network_state)
++            if nm_conf_content:
++                util.write_file(nm_conf_path, nm_conf_content)
+         if self.netrules_path:
+             netrules_content = self._render_persistent_net(network_state)
+             netrules_path = os.path.join(target, self.netrules_path)
+diff --git a/tests/unittests/test_net.py b/tests/unittests/test_net.py
+index 172d6046..379ac8bb 100755
+--- a/tests/unittests/test_net.py
++++ b/tests/unittests/test_net.py
+@@ -162,6 +162,13 @@ NETMASK0=0.0.0.0
+ ;
+ nameserver 172.19.0.12
+ """.lstrip()),
++            ('etc/NetworkManager/conf.d/99-cloud-init.conf',
++             """
++# Created by cloud-init on instance boot automatically, do not edit.
++#
++[main]
++dns = none
++""".lstrip()),
+             ('etc/udev/rules.d/70-persistent-net.rules',
+              "".join(['SUBSYSTEM=="net", ACTION=="add", DRIVERS=="?*", ',
+                       'ATTR{address}=="fa:16:3e:ed:9a:59", NAME="eth0"\n']))]
+@@ -222,6 +229,13 @@ USERCTL=no
+ ;
+ nameserver 172.19.0.12
+ """.lstrip()),
++            ('etc/NetworkManager/conf.d/99-cloud-init.conf',
++             """
++# Created by cloud-init on instance boot automatically, do not edit.
++#
++[main]
++dns = none
++""".lstrip()),
+             ('etc/udev/rules.d/70-persistent-net.rules',
+              "".join(['SUBSYSTEM=="net", ACTION=="add", DRIVERS=="?*", ',
+                       'ATTR{address}=="fa:16:3e:ed:9a:59", NAME="eth0"\n']))]
+@@ -304,6 +318,13 @@ USERCTL=no
+ ;
+ nameserver 172.19.0.12
+ """.lstrip()),
++            ('etc/NetworkManager/conf.d/99-cloud-init.conf',
++             """
++# Created by cloud-init on instance boot automatically, do not edit.
++#
++[main]
++dns = none
++""".lstrip()),
+             ('etc/udev/rules.d/70-persistent-net.rules',
+              "".join(['SUBSYSTEM=="net", ACTION=="add", DRIVERS=="?*", ',
+                       'ATTR{address}=="fa:16:3e:ed:9a:59", NAME="eth0"\n']))]
+-- 
+2.13.6
+
diff --git a/SOURCES/0032-sysconfig-Render-IPV6_DEFAULTGW-correctly.patch b/SOURCES/0032-sysconfig-Render-IPV6_DEFAULTGW-correctly.patch
deleted file mode 100644
index b4ab937..0000000
--- a/SOURCES/0032-sysconfig-Render-IPV6_DEFAULTGW-correctly.patch
+++ /dev/null
@@ -1,64 +0,0 @@
-From 75fbba4601d09112615c342f88ef7b43fead0508 Mon Sep 17 00:00:00 2001
-From: Ryan McCabe <rmccabe@redhat.com>
-Date: Fri, 2 Feb 2018 10:25:31 -0500
-Subject: [PATCH] sysconfig: Render IPV6_DEFAULTGW correctly
-
-Downstream backport of the fixes introduced in upstream commit
-97abd83513bee191b58f095f4d683b18acce0b49 which will not apply to
-the RHEL 0.7.9 tree.
-
-Signed-off-by: Ryan McCabe <rmccabe@redhat.com>
-Resolves: rhbz#1540094
----
- cloudinit/net/sysconfig.py                     | 6 +++++-
- tests/unittests/test_distros/test_netconfig.py | 4 ++++
- 2 files changed, 9 insertions(+), 1 deletion(-)
-
-diff --git a/cloudinit/net/sysconfig.py b/cloudinit/net/sysconfig.py
-index 09df76e3..9975fe2c 100644
---- a/cloudinit/net/sysconfig.py
-+++ b/cloudinit/net/sysconfig.py
-@@ -348,7 +348,11 @@ class Renderer(renderer.Renderer):
-                                 subnet['netmask']
- 
-                 if 'gateway' in subnet:
--                    iface_cfg['GATEWAY'] = subnet['gateway']
-+                    iface_cfg['DEFROUTE'] = True
-+                    if ":" in subnet['gateway']:
-+                        iface_cfg['IPV6_DEFAULTGW'] = subnet['gateway']
-+                    else:
-+                        iface_cfg['GATEWAY'] = subnet['gateway']
- 
-     @classmethod
-     def _render_subnet_routes(cls, iface_cfg, route_cfg, subnets):
-diff --git a/tests/unittests/test_distros/test_netconfig.py b/tests/unittests/test_distros/test_netconfig.py
-index 861cf8ef..10e25a72 100644
---- a/tests/unittests/test_distros/test_netconfig.py
-+++ b/tests/unittests/test_distros/test_netconfig.py
-@@ -257,9 +257,11 @@ NETWORKING=yes
- # Created by cloud-init on instance boot automatically, do not edit.
- #
- BOOTPROTO=none
-+DEFROUTE=yes
- DEVICE=eth0
- IPADDR=192.168.1.5
- NETMASK=255.255.255.0
-+GATEWAY=192.168.1.254
- ONBOOT=yes
- TYPE=Ethernet
- USERCTL=no
-@@ -394,9 +396,11 @@ IPV6_AUTOCONF=no
- # Created by cloud-init on instance boot automatically, do not edit.
- #
- BOOTPROTO=none
-+DEFROUTE=yes
- DEVICE=eth0
- IPV6ADDR=2607:f0d0:1002:0011::2/64
- IPV6INIT=yes
-+IPV6_DEFAULTGW=2607:f0d0:1002:0011::1
- ONBOOT=yes
- TYPE=Ethernet
- USERCTL=no
--- 
-2.14.3
-
diff --git a/SOURCES/0033-Render-DNS-and-DOMAIN-lines-for-sysconfig.patch b/SOURCES/0033-Render-DNS-and-DOMAIN-lines-for-sysconfig.patch
new file mode 100644
index 0000000..ee129d4
--- /dev/null
+++ b/SOURCES/0033-Render-DNS-and-DOMAIN-lines-for-sysconfig.patch
@@ -0,0 +1,89 @@
+From 3774ec0a9873b4dfbc647ee57f16fe461706c1b2 Mon Sep 17 00:00:00 2001
+From: Ryan McCabe <rmccabe@redhat.com>
+Date: Tue, 21 Nov 2017 11:50:18 -0500
+Subject: [PATCH] Render DNS and DOMAIN lines for sysconfig
+
+Currently when dns and dns search info is provided, it is not
+rendered when outputting to sysconfig format.
+
+This patch causes the DNS and DOMAIN lines to be written out rendering
+sysconfig.
+
+This is a backport of upstream commit
+bbe91cdc6917adb503b455e6860c21ea7b3f567f which will not apply to the
+0.7.9 tree.
+
+Signed-off-by: Ryan McCabe <rmccabe@redhat.com>
+Resolves: rhbz#1489270
+---
+ cloudinit/net/sysconfig.py  | 17 +++++++++++++++++
+ tests/unittests/test_net.py | 11 ++++++++---
+ 2 files changed, 25 insertions(+), 3 deletions(-)
+
+diff --git a/cloudinit/net/sysconfig.py b/cloudinit/net/sysconfig.py
+index ca031691..e34c8491 100644
+--- a/cloudinit/net/sysconfig.py
++++ b/cloudinit/net/sysconfig.py
+@@ -350,6 +350,23 @@ class Renderer(renderer.Renderer):
+                 if 'gateway' in subnet:
+                     iface_cfg['GATEWAY'] = subnet['gateway']
+ 
++                if 'dns_search' in subnet:
++                    if isinstance(subnet['dns_search'], (list, tuple)):
++                        # Currently limited to 6 entries per resolv.conf(5)
++                        search_list = subnet['dns_search'][:6]
++                        iface_cfg['DOMAIN'] = ' '.join(search_list)
++                    else:
++                        iface_cfg['DOMAIN'] = subnet['dns_search']
++
++                if 'dns_nameservers' in subnet:
++                    if isinstance(subnet['dns_search'], (list, tuple)):
++                        # Currently limited to 3 entries per resolv.conf(5)
++                        dns_list = subnet['dns_nameservers'][:3]
++                        for i, k in enumerate(dns_list, 1):
++                            iface_cfg['DNS' + str(i)] = k
++                    else:
++                        iface_cfg['DNS1'] = subnet['dns_nameservers']
++
+     @classmethod
+     def _render_subnet_routes(cls, iface_cfg, route_cfg, subnets):
+         for i, subnet in enumerate(subnets, start=len(iface_cfg.children)):
+diff --git a/tests/unittests/test_net.py b/tests/unittests/test_net.py
+index 4a32eb88..551370d4 100644
+--- a/tests/unittests/test_net.py
++++ b/tests/unittests/test_net.py
+@@ -820,7 +820,9 @@ USERCTL=no
+ 
+     def test_network_config_v1_samples(self):
+         ns = network_state.parse_net_config_data(CONFIG_V1_SIMPLE_SUBNET)
+-        render_dir = self.tmp_path("render")
++        tmp_dir = tempfile.mkdtemp()
++        self.addCleanup(shutil.rmtree, tmp_dir)
++        render_dir = os.path.join(tmp_dir, "render")
+         os.makedirs(render_dir)
+         renderer = sysconfig.Renderer()
+         renderer.render_network_state(render_dir, ns)
+@@ -844,7 +846,9 @@ USERCTL=no
+ 
+     def test_config_with_explicit_loopback(self):
+         ns = network_state.parse_net_config_data(CONFIG_V1_EXPLICIT_LOOPBACK)
+-        render_dir = self.tmp_path("render")
++        tmp_dir = tempfile.mkdtemp()
++        self.addCleanup(shutil.rmtree, tmp_dir)
++        render_dir = os.path.join(tmp_dir, "render")
+         os.makedirs(render_dir)
+         renderer = sysconfig.Renderer()
+         renderer.render_network_state(render_dir, ns)
+@@ -904,7 +908,8 @@ iface eth1000 inet dhcp
+         self.assertEqual(expected.lstrip(), contents.lstrip())
+ 
+     def test_config_with_explicit_loopback(self):
+-        tmp_dir = self.tmp_dir()
++        tmp_dir = tempfile.mkdtemp()
++        self.addCleanup(shutil.rmtree, tmp_dir)
+         ns = network_state.parse_net_config_data(CONFIG_V1_EXPLICIT_LOOPBACK)
+         renderer = eni.Renderer()
+         renderer.render_network_state(tmp_dir, ns)
+-- 
+2.13.6
+
diff --git a/SOURCES/0033-sysconfig-Render-DNS-and-DOMAIN.patch b/SOURCES/0033-sysconfig-Render-DNS-and-DOMAIN.patch
deleted file mode 100644
index 3c2c3cc..0000000
--- a/SOURCES/0033-sysconfig-Render-DNS-and-DOMAIN.patch
+++ /dev/null
@@ -1,86 +0,0 @@
-From 6f54ccf28a327174df663ea2e07f32d7e632fddd Mon Sep 17 00:00:00 2001
-From: Ryan McCabe <rmccabe@redhat.com>
-Date: Thu, 15 Feb 2018 10:30:40 -0500
-Subject: [PATCH] sysconfig: Render DNS and DOMAIN
-
-Currently when dns and dns search info is provided, it is not
-rendered when outputting to sysconfig format.
-
-This patch causes the DNS and DOMAIN lines to be written out rendering
-sysconfig.
-
-This is a backport of upstream commit
-bbe91cdc6917adb503b455e6860c21ea7b3f567f which will not apply to the
-0.7.9 tree.
-
-Signed-off-by: Ryan McCabe <rmccabe@redhat.com>
-Resolves: rhbz#1545525
----
- cloudinit/net/sysconfig.py  | 17 +++++++++++++++++
- tests/unittests/test_net.py |  8 +++++---
- 2 files changed, 22 insertions(+), 3 deletions(-)
-
-diff --git a/cloudinit/net/sysconfig.py b/cloudinit/net/sysconfig.py
-index 9975fe2c..ec412512 100644
---- a/cloudinit/net/sysconfig.py
-+++ b/cloudinit/net/sysconfig.py
-@@ -354,6 +354,23 @@ class Renderer(renderer.Renderer):
-                     else:
-                         iface_cfg['GATEWAY'] = subnet['gateway']
- 
-+                if 'dns_search' in subnet:
-+                    if isinstance(subnet['dns_search'], (list, tuple)):
-+                        # Currently limited to 6 entries per resolv.conf(5)
-+                        search_list = subnet['dns_search'][:6]
-+                        iface_cfg['DOMAIN'] = ' '.join(search_list)
-+                    else:
-+                        iface_cfg['DOMAIN'] = subnet['dns_search']
-+
-+                if 'dns_nameservers' in subnet:
-+                    if isinstance(subnet['dns_nameservers'], (list, tuple)):
-+                        # Currently limited to 3 entries per resolv.conf(5)
-+                        dns_list = subnet['dns_nameservers'][:3]
-+                        for i, k in enumerate(dns_list, 1):
-+                            iface_cfg['DNS' + str(i)] = k
-+                    else:
-+                        iface_cfg['DNS1'] = subnet['dns_nameservers']
-+
-     @classmethod
-     def _render_subnet_routes(cls, iface_cfg, route_cfg, subnets):
-         for i, subnet in enumerate(subnets, start=len(iface_cfg.children)):
-diff --git a/tests/unittests/test_net.py b/tests/unittests/test_net.py
-index d75742be..f2a1998a 100644
---- a/tests/unittests/test_net.py
-+++ b/tests/unittests/test_net.py
-@@ -780,7 +780,9 @@ USERCTL=no
- 
-     def test_config_with_explicit_loopback(self):
-         ns = network_state.parse_net_config_data(CONFIG_V1_EXPLICIT_LOOPBACK)
--        render_dir = self.tmp_path("render")
-+        tmp_dir = tempfile.mkdtemp()
-+        self.addCleanup(shutil.rmtree, tmp_dir)
-+        render_dir = os.path.join(tmp_dir, "render")
-         os.makedirs(render_dir)
-         renderer = sysconfig.Renderer()
-         renderer.render_network_state(render_dir, ns)
-@@ -792,7 +794,6 @@ USERCTL=no
- #
- BOOTPROTO=dhcp
- DEVICE=eth0
--NM_CONTROLLED=no
- ONBOOT=yes
- TYPE=Ethernet
- USERCTL=no
-@@ -841,7 +842,8 @@ iface eth1000 inet dhcp
-         self.assertEqual(expected.lstrip(), contents.lstrip())
- 
-     def test_config_with_explicit_loopback(self):
--        tmp_dir = self.tmp_dir()
-+        tmp_dir = tempfile.mkdtemp()
-+        self.addCleanup(shutil.rmtree, tmp_dir)
-         ns = network_state.parse_net_config_data(CONFIG_V1_EXPLICIT_LOOPBACK)
-         renderer = eni.Renderer()
-         renderer.render_network_state(tmp_dir, ns)
--- 
-2.14.3
-
diff --git a/SOURCES/0034-Start_cloud_init_after_dbus.patch b/SOURCES/0034-Start_cloud_init_after_dbus.patch
new file mode 100644
index 0000000..06419c0
--- /dev/null
+++ b/SOURCES/0034-Start_cloud_init_after_dbus.patch
@@ -0,0 +1,18 @@
+diff --git a/rhel/systemd/cloud-init-local.service b/rhel/systemd/cloud-init-local.service
+index 047907c4..656eddb9 100644
+--- a/rhel/systemd/cloud-init-local.service
++++ b/rhel/systemd/cloud-init-local.service
+@@ -3,10 +3,12 @@ Description=Initial cloud-init job (pre-networking)
+ DefaultDependencies=no
+ Wants=network-pre.target
+ After=systemd-remount-fs.service
++Requires=dbus.socket
++After=dbus.socket
+ Before=NetworkManager.service network.service
+ Before=network-pre.target
+ Before=shutdown.target
+-Before=sysinit.target
++Before=firewalld.target
+ Conflicts=shutdown.target
+ RequiresMountsFor=/var/lib/cloud
+ ConditionPathExists=!/etc/cloud/cloud-init.disabled
diff --git a/SOURCES/0035-sysconfig-Render-IPV6_DEFAULTGW-correctly.patch b/SOURCES/0035-sysconfig-Render-IPV6_DEFAULTGW-correctly.patch
new file mode 100644
index 0000000..8aaa988
--- /dev/null
+++ b/SOURCES/0035-sysconfig-Render-IPV6_DEFAULTGW-correctly.patch
@@ -0,0 +1,74 @@
+From cee7eef674c4fe7e4a23e5c358df23064796e9e2 Mon Sep 17 00:00:00 2001
+From: Ryan McCabe <rmccabe@redhat.com>
+Date: Thu, 30 Nov 2017 08:59:03 -0500
+Subject: [PATCH] sysconfig: Render IPV6_DEFAULTGW correctly
+
+Downstream backport of the fixes introduced in upstream commit
+97abd83513bee191b58f095f4d683b18acce0b49 which will not apply to
+the RHEL 0.7.9 tree.
+
+Signed-off-by: Ryan McCabe <rmccabe@redhat.com>
+Resolves: rhbz#1492726
+---
+ cloudinit/net/sysconfig.py                     | 6 +++++-
+ tests/unittests/test_distros/test_netconfig.py | 4 +++-
+ tests/unittests/test_net.py                    | 1 +
+ 3 files changed, 9 insertions(+), 2 deletions(-)
+
+diff --git a/cloudinit/net/sysconfig.py b/cloudinit/net/sysconfig.py
+index e34c8491..380daedf 100644
+--- a/cloudinit/net/sysconfig.py
++++ b/cloudinit/net/sysconfig.py
+@@ -348,7 +348,11 @@ class Renderer(renderer.Renderer):
+                                 subnet['netmask']
+ 
+                 if 'gateway' in subnet:
+-                    iface_cfg['GATEWAY'] = subnet['gateway']
++                    iface_cfg['DEFROUTE'] = True
++                    if ":" in subnet['gateway']:
++                        iface_cfg['IPV6_DEFAULTGW'] = subnet['gateway']
++                    else:
++                        iface_cfg['GATEWAY'] = subnet['gateway']
+ 
+                 if 'dns_search' in subnet:
+                     if isinstance(subnet['dns_search'], (list, tuple)):
+diff --git a/tests/unittests/test_distros/test_netconfig.py b/tests/unittests/test_distros/test_netconfig.py
+index 108d5741..10e25a72 100644
+--- a/tests/unittests/test_distros/test_netconfig.py
++++ b/tests/unittests/test_distros/test_netconfig.py
+@@ -257,6 +257,7 @@ NETWORKING=yes
+ # Created by cloud-init on instance boot automatically, do not edit.
+ #
+ BOOTPROTO=none
++DEFROUTE=yes
+ DEVICE=eth0
+ IPADDR=192.168.1.5
+ NETMASK=255.255.255.0
+@@ -395,10 +396,11 @@ IPV6_AUTOCONF=no
+ # Created by cloud-init on instance boot automatically, do not edit.
+ #
+ BOOTPROTO=none
++DEFROUTE=yes
+ DEVICE=eth0
+ IPV6ADDR=2607:f0d0:1002:0011::2/64
+-GATEWAY=2607:f0d0:1002:0011::1
+ IPV6INIT=yes
++IPV6_DEFAULTGW=2607:f0d0:1002:0011::1
+ ONBOOT=yes
+ TYPE=Ethernet
+ USERCTL=no
+diff --git a/tests/unittests/test_net.py b/tests/unittests/test_net.py
+index 551370d4..34af5daa 100644
+--- a/tests/unittests/test_net.py
++++ b/tests/unittests/test_net.py
+@@ -833,6 +833,7 @@ USERCTL=no
+ # Created by cloud-init on instance boot automatically, do not edit.
+ #
+ BOOTPROTO=none
++DEFROUTE=yes
+ DEVICE=interface0
+ GATEWAY=10.0.2.2
+ HWADDR=52:54:00:12:34:00
+-- 
+2.14.3
+
diff --git a/SOURCES/0036-sysconfig-Don-t-write-BOOTPROTO-dhcp-for-ipv6-dhcp.patch b/SOURCES/0036-sysconfig-Don-t-write-BOOTPROTO-dhcp-for-ipv6-dhcp.patch
new file mode 100644
index 0000000..b34b8bc
--- /dev/null
+++ b/SOURCES/0036-sysconfig-Don-t-write-BOOTPROTO-dhcp-for-ipv6-dhcp.patch
@@ -0,0 +1,32 @@
+From cbc09ba2f8e6fa967232039e6f6a3363d54ba592 Mon Sep 17 00:00:00 2001
+From: Ryan McCabe <rmccabe@redhat.com>
+Date: Mon, 4 Dec 2017 13:43:30 -0500
+Subject: [PATCH] sysconfig: Don't write BOOTPROTO=dhcp for ipv6 dhcp
+
+Don't write BOOTPROTO=dhcp for ipv6 dhcp, as BOOTPROTO applies
+only to ipv4. Explicitly write IPV6_AUTOCONF=no for dhcp on ipv6.
+
+X-downstream-only: yes
+
+Resolves: rhbz#1519271
+Signed-off-by: Ryan McCabe <rmccabe@redhat.com>
+---
+ cloudinit/net/sysconfig.py | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/cloudinit/net/sysconfig.py b/cloudinit/net/sysconfig.py
+index e34c8491..bff844cd 100644
+--- a/cloudinit/net/sysconfig.py
++++ b/cloudinit/net/sysconfig.py
+@@ -287,7 +287,7 @@ class Renderer(renderer.Renderer):
+             if subnet_type == 'dhcp6':
+                 iface_cfg['IPV6INIT'] = True
+                 iface_cfg['DHCPV6C'] = True
+-                iface_cfg['BOOTPROTO'] = 'dhcp'
++                iface_cfg['IPV6_AUTOCONF'] = False
+             elif subnet_type in ['dhcp4', 'dhcp']:
+                 iface_cfg['BOOTPROTO'] = 'dhcp'
+             elif subnet_type == 'static':
+-- 
+2.14.3
+
diff --git a/SOURCES/0037-sysconfig-Fix-traceback.patch b/SOURCES/0037-sysconfig-Fix-traceback.patch
new file mode 100644
index 0000000..433614e
--- /dev/null
+++ b/SOURCES/0037-sysconfig-Fix-traceback.patch
@@ -0,0 +1,30 @@
+From a11ca2707b4b1330d3e4c83c6cf37295909bd0fc Mon Sep 17 00:00:00 2001
+From: Ryan McCabe <rmccabe@redhat.com>
+Date: Mon, 15 Jan 2018 09:23:09 -0500
+Subject: [PATCH] sysconfig: Fix traceback
+
+Fix a typo that caused a traceback in some situations that was introduced
+as part of the fix for rhbz#1489270
+
+Signed-off-by: Ryan McCabe <rmccabe@redhat.com>
+Resolves: rhbz#1489270
+---
+ cloudinit/net/sysconfig.py | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/cloudinit/net/sysconfig.py b/cloudinit/net/sysconfig.py
+index bff844cd..3c5d0615 100644
+--- a/cloudinit/net/sysconfig.py
++++ b/cloudinit/net/sysconfig.py
+@@ -359,7 +359,7 @@ class Renderer(renderer.Renderer):
+                         iface_cfg['DOMAIN'] = subnet['dns_search']
+ 
+                 if 'dns_nameservers' in subnet:
+-                    if isinstance(subnet['dns_search'], (list, tuple)):
++                    if isinstance(subnet['dns_nameservers'], (list, tuple)):
+                         # Currently limited to 3 entries per resolv.conf(5)
+                         dns_list = subnet['dns_nameservers'][:3]
+                         for i, k in enumerate(dns_list, 1):
+-- 
+2.14.3
+
diff --git a/SOURCES/0038-Fix-bug-that-resulted-in-an-attempt-to-rename-bonds.patch b/SOURCES/0038-Fix-bug-that-resulted-in-an-attempt-to-rename-bonds.patch
new file mode 100644
index 0000000..d79bbc8
--- /dev/null
+++ b/SOURCES/0038-Fix-bug-that-resulted-in-an-attempt-to-rename-bonds.patch
@@ -0,0 +1,252 @@
+From 4ada8f2ec17d11835b44ab3d7786e5f3a732df41 Mon Sep 17 00:00:00 2001
+From: Scott Moser <smoser@brickies.net>
+Date: Fri, 31 Mar 2017 10:56:04 -0400
+Subject: [PATCH] Fix bug that resulted in an attempt to rename bonds or vlans.
+
+When cloud-init ran in the init stage (after networking had come up).
+A bug could occur where cloud-init would attempt and fail to rename
+network devices that had "inherited" mac addresses.
+
+The intent of apply_network_config_names was always to rename only
+the devices that were "physical" per the network config.  (This would
+include veth devices in a container).  The bug was in creating
+the dictionary of interfaces by mac address.  If there were multiple
+interfaces with the same mac address then renames could fail.
+This situation was guaranteed to occur with bonds or vlans or other
+devices that inherit their mac.
+
+The solution is to change get_interfaces_by_mac to skip interfaces
+that have an inherited mac.
+
+Also drop the 'devs' argument to get_interfaces_by_mac.  It was
+non-obvious what the result should be if a device in the input
+list was filtered out. ie should the following have an entry for
+bond0 or not.  get_interfaces_by_mac(devs=['bond0'])
+
+LP: #1669860
+(cherry picked from commit bf7723e8092bb1f8a442aa2399dd870e130a27d9)
+
+Resolves: rhbz#1512247
+Signed-off-by: Ryan McCabe <rmccabe@redhat.com>
+(cherry picked from commit f9e8f13f916fe740e46c9a0e9dd2dbb3cdb39975)
+---
+ cloudinit/net/__init__.py   | 78 ++++++++++++++++++++++++++++++++++-----------
+ tests/unittests/test_net.py | 76 +++++++++++++++++++++++++++++++++++++++++++
+ 2 files changed, 135 insertions(+), 19 deletions(-)
+ mode change 100755 => 100644 cloudinit/net/__init__.py
+
+diff --git a/cloudinit/net/__init__.py b/cloudinit/net/__init__.py
+old mode 100755
+new mode 100644
+index ea649cc2..ab7e8996
+--- a/cloudinit/net/__init__.py
++++ b/cloudinit/net/__init__.py
+@@ -82,6 +82,10 @@ def is_wireless(devname):
+     return os.path.exists(sys_dev_path(devname, "wireless"))
+ 
+ 
++def is_bridge(devname):
++    return os.path.exists(sys_dev_path(devname, "bridge"))
++
++
+ def is_connected(devname):
+     # is_connected isn't really as simple as that.  2 is
+     # 'physically connected'. 3 is 'not connected'. but a wlan interface will
+@@ -132,7 +136,7 @@ def generate_fallback_config():
+     for interface in potential_interfaces:
+         if interface.startswith("veth"):
+             continue
+-        if os.path.exists(sys_dev_path(interface, "bridge")):
++        if is_bridge(interface):
+             # skip any bridges
+             continue
+         carrier = read_sys_net_int(interface, 'carrier')
+@@ -187,7 +191,11 @@ def apply_network_config_names(netcfg, strict_present=True, strict_busy=True):
+     """read the network config and rename devices accordingly.
+     if strict_present is false, then do not raise exception if no devices
+     match.  if strict_busy is false, then do not raise exception if the
+-    device cannot be renamed because it is currently configured."""
++    device cannot be renamed because it is currently configured.
++
++    renames are only attempted for interfaces of type 'physical'.  It is
++    expected that the network system will create other devices with the
++    correct name in place."""
+     renames = []
+     for ent in netcfg.get('config', {}):
+         if ent.get('type') != 'physical':
+@@ -201,13 +209,35 @@ def apply_network_config_names(netcfg, strict_present=True, strict_busy=True):
+     return _rename_interfaces(renames)
+ 
+ 
++def interface_has_own_mac(ifname, strict=False):
++    """return True if the provided interface has its own address.
++
++    Based on addr_assign_type in /sys.  Return true for any interface
++    that does not have a 'stolen' address. Examples of such devices
++    are bonds or vlans that inherit their mac from another device.
++    Possible values are:
++      0: permanent address    2: stolen from another device
++      1: randomly generated   3: set using dev_set_mac_address"""
++
++    assign_type = read_sys_net_int(ifname, "addr_assign_type")
++    if strict and assign_type is None:
++        raise ValueError("%s had no addr_assign_type.")
++    return assign_type in (0, 1, 3)
++
++
+ def _get_current_rename_info(check_downable=True):
+-    """Collect information necessary for rename_interfaces."""
+-    names = get_devicelist()
++    """Collect information necessary for rename_interfaces.
++
++    returns a dictionary by mac address like:
++       {mac:
++         {'name': name
++          'up': boolean: is_up(name),
++          'downable': None or boolean indicating that the
++                      device has only automatically assigned ip addrs.}}
++    """
+     bymac = {}
+-    for n in names:
+-        bymac[get_interface_mac(n)] = {
+-            'name': n, 'up': is_up(n), 'downable': None}
++    for mac, name in get_interfaces_by_mac().items():
++        bymac[mac] = {'name': name, 'up': is_up(name), 'downable': None}
+ 
+     if check_downable:
+         nmatch = re.compile(r"[0-9]+:\s+(\w+)[@:]")
+@@ -346,22 +376,32 @@ def get_interface_mac(ifname):
+     return read_sys_net_safe(ifname, path)
+ 
+ 
+-def get_interfaces_by_mac(devs=None):
+-    """Build a dictionary of tuples {mac: name}"""
+-    if devs is None:
+-        try:
+-            devs = get_devicelist()
+-        except OSError as e:
+-            if e.errno == errno.ENOENT:
+-                devs = []
+-            else:
+-                raise
++def get_interfaces_by_mac():
++    """Build a dictionary of tuples {mac: name}.
++
++    Bridges and any devices that have a 'stolen' mac are excluded."""
++    try:
++        devs = get_devicelist()
++    except OSError as e:
++        if e.errno == errno.ENOENT:
++            devs = []
++        else:
++            raise
+     ret = {}
+     for name in devs:
++        if not interface_has_own_mac(name):
++            continue
++        if is_bridge(name):
++            continue
+         mac = get_interface_mac(name)
+         # some devices may not have a mac (tun0)
+-        if mac:
+-            ret[mac] = name
++        if not mac:
++            continue
++        if mac in ret:
++            raise RuntimeError(
++                "duplicate mac found! both '%s' and '%s' have mac '%s'" %
++                (name, ret[mac], mac))
++        ret[mac] = name
+     return ret
+ 
+ # vi: ts=4 expandtab
+diff --git a/tests/unittests/test_net.py b/tests/unittests/test_net.py
+index 551370d4..cadaf596 100644
+--- a/tests/unittests/test_net.py
++++ b/tests/unittests/test_net.py
+@@ -1173,6 +1173,82 @@ class TestEniRoundTrip(TestCase):
+             expected, [line for line in found if line])
+ 
+ 
++class TestGetInterfacesByMac(TestCase):
++    _data = {'devices': ['enp0s1', 'enp0s2', 'bond1', 'bridge1',
++                         'bridge1-nic', 'tun0'],
++             'bonds': ['bond1'],
++             'bridges': ['bridge1'],
++             'own_macs': ['enp0s1', 'enp0s2', 'bridge1-nic', 'bridge1'],
++             'macs': {'enp0s1': 'aa:aa:aa:aa:aa:01',
++                      'enp0s2': 'aa:aa:aa:aa:aa:02',
++                      'bond1': 'aa:aa:aa:aa:aa:01',
++                      'bridge1': 'aa:aa:aa:aa:aa:03',
++                      'bridge1-nic': 'aa:aa:aa:aa:aa:03',
++                      'tun0': None}}
++    data = {}
++
++    def _se_get_devicelist(self):
++        return self.data['devices']
++
++    def _se_get_interface_mac(self, name):
++        return self.data['macs'][name]
++
++    def _se_is_bridge(self, name):
++        return name in self.data['bridges']
++
++    def _se_interface_has_own_mac(self, name):
++        return name in self.data['own_macs']
++
++    def _mock_setup(self):
++        self.data = copy.deepcopy(self._data)
++        mocks = ('get_devicelist', 'get_interface_mac', 'is_bridge',
++                 'interface_has_own_mac')
++        self.mocks = {}
++        for n in mocks:
++            m = mock.patch('cloudinit.net.' + n,
++                           side_effect=getattr(self, '_se_' + n))
++            self.addCleanup(m.stop)
++            self.mocks[n] = m.start()
++
++    def test_raise_exception_on_duplicate_macs(self):
++        self._mock_setup()
++        self.data['macs']['bridge1-nic'] = self.data['macs']['enp0s1']
++        self.assertRaises(RuntimeError, net.get_interfaces_by_mac)
++
++    def test_excludes_any_without_mac_address(self):
++        self._mock_setup()
++        ret = net.get_interfaces_by_mac()
++        self.assertIn('tun0', self._se_get_devicelist())
++        self.assertNotIn('tun0', ret.values())
++
++    def test_excludes_stolen_macs(self):
++        self._mock_setup()
++        ret = net.get_interfaces_by_mac()
++        self.mocks['interface_has_own_mac'].assert_has_calls(
++            [mock.call('enp0s1'), mock.call('bond1')], any_order=True)
++        self.assertEqual(
++            {'aa:aa:aa:aa:aa:01': 'enp0s1', 'aa:aa:aa:aa:aa:02': 'enp0s2',
++             'aa:aa:aa:aa:aa:03': 'bridge1-nic'},
++            ret)
++
++    def test_excludes_bridges(self):
++        self._mock_setup()
++        # add a device 'b1', make all return they have their "own mac",
++        # set everything other than 'b1' to be a bridge.
++        # then expect b1 is the only thing left.
++        self.data['macs']['b1'] = 'aa:aa:aa:aa:aa:b1'
++        self.data['devices'].append('b1')
++        self.data['bonds'] = []
++        self.data['own_macs'] = self.data['devices']
++        self.data['bridges'] = [f for f in self.data['devices'] if f != "b1"]
++        ret = net.get_interfaces_by_mac()
++        self.assertEqual({'aa:aa:aa:aa:aa:b1': 'b1'}, ret)
++        self.mocks['is_bridge'].assert_has_calls(
++            [mock.call('bridge1'), mock.call('enp0s1'), mock.call('bond1'),
++             mock.call('b1')],
++            any_order=True)
++
++
+ def _gzip_data(data):
+     with io.BytesIO() as iobuf:
+         gzfp = gzip.GzipFile(mode="wb", fileobj=iobuf)
+-- 
+2.14.3
+
diff --git a/SOURCES/0039-azure-Fix-publishing-of-hostname.patch b/SOURCES/0039-azure-Fix-publishing-of-hostname.patch
new file mode 100644
index 0000000..f4cb744
--- /dev/null
+++ b/SOURCES/0039-azure-Fix-publishing-of-hostname.patch
@@ -0,0 +1,33 @@
+From d3a5f6f36ca673bc69d2e020c65fb85bf0dfc519 Mon Sep 17 00:00:00 2001
+From: Ryan McCabe <rmccabe@redhat.com>
+Date: Tue, 13 Feb 2018 22:42:17 -0500
+Subject: [PATCH] azure: Fix publishing of hostname
+
+Set the DHCP_HOSTNAME env var before bouncing the interfaces
+to ensure that the hostname is published correctly.
+
+Resolves: rhbz#1434109
+
+X-downstream-only: yes
+Signed-off-by: Ryan McCabe <rmccabe@redhat.com>
+---
+ cloudinit/sources/DataSourceAzure.py | 3 ++-
+ 1 file changed, 2 insertions(+), 1 deletion(-)
+
+diff --git a/cloudinit/sources/DataSourceAzure.py b/cloudinit/sources/DataSourceAzure.py
+index 48a3e1df..a0ebd2ba 100644
+--- a/cloudinit/sources/DataSourceAzure.py
++++ b/cloudinit/sources/DataSourceAzure.py
+@@ -27,7 +27,8 @@ AGENT_START = ['service', 'walinuxagent', 'start']
+ AGENT_START_BUILTIN = "__builtin__"
+ BOUNCE_COMMAND = [
+     'sh', '-xc',
+-    "i=$interface; x=0; ifdown $i || x=$?; ifup $i || x=$?; exit $x"
++    "i=$interface; DHCP_HOSTNAME=$hostname; x=0; "
++    "ifdown $i || x=$?; ifup $i || x=$?; exit $x"
+ ]
+ # azure systems will always have a resource disk, and 66-azure-ephemeral.rules
+ # ensures that it gets linked to this path.
+-- 
+2.14.3
+
diff --git a/SPECS/cloud-init.spec b/SPECS/cloud-init.spec
index 2eb667e..7fffb3c 100644
--- a/SPECS/cloud-init.spec
+++ b/SPECS/cloud-init.spec
@@ -7,7 +7,7 @@
 
 Name:           cloud-init
 Version:        0.7.9
-Release:        9%{?dist}.6
+Release:        24%{?dist}
 Summary:        Cloud instance init scripts
 
 Group:          System Environment/Base
@@ -42,20 +42,26 @@ Patch0017: 0017-sysconfig-Raise-ValueError-when-multiple-default-gat.patch
 Patch0018: 0018-Fix-dual-stack-IPv4-IPv6-configuration-for-RHEL.patch
 Patch0019: 0019-Add-missing-sysconfig-unit-test-data.patch
 Patch0020: 0020-Fix-ipv6-subnet-detection.patch
+# Not applied to work around additional issues related to rhbz#1474226
 #Patch0021: 0021-azure-ensure-that-networkmanager-hook-script-runs.patch
 Patch0022: 0022-RHEL-CentOS-Fix-default-routes-for-IPv4-IPv6-configu.patch
 Patch0023: 0023-DatasourceEc2-add-warning-message-when-not-on-AWS.patch
 Patch0024: 0024-Identify-Brightbox-as-an-Ec2-datasource-user.patch
 Patch0025: 0025-AliYun-Enable-platform-identification-and-enable-by-.patch
 Patch0026: 0026-Fix-alibaba-cloud-unit-tests-to-work-with-0.7.9.patch
-Patch0027: 0027-systemd-create-run-cloud-init-enabled.patch
-Patch0028: 0028-net-Allow-for-NetworkManager-configuration.patch
+Patch0027: 0027-Fix-eni-rendering-of-multiple-IPs-per-interface.patch
+Patch0028: 0028-systemd-create-run-cloud-init-enabled.patch
 Patch0029: 0029-support-loopback-as-a-device-type.patch
-Patch0030: 0030-Render-the-GATEWAY-value-in-interface-files-which-ha.patch
-Patch0031: 0031-sysconfig-Don-t-write-BOOTPROTO-dhcp-for-ipv6-dhcp.patch
-Patch0032: 0032-sysconfig-Render-IPV6_DEFAULTGW-correctly.patch
-Patch0033: 0033-sysconfig-Render-DNS-and-DOMAIN.patch
-
+Patch0030: 0030-sysconfig-include-GATEWAY-value-if-set-in-subnet.patch
+Patch0031: 0031-rh_subscription-Perform-null-checks-for-enabled-and-.patch
+Patch0032: 0032-net-Allow-for-NetworkManager-configuration.patch
+Patch0033: 0033-Render-DNS-and-DOMAIN-lines-for-sysconfig.patch
+Patch0034: 0034-Start_cloud_init_after_dbus.patch
+Patch0035: 0035-sysconfig-Render-IPV6_DEFAULTGW-correctly.patch
+Patch0036: 0036-sysconfig-Don-t-write-BOOTPROTO-dhcp-for-ipv6-dhcp.patch
+Patch0037: 0037-sysconfig-Fix-traceback.patch
+Patch0038: 0038-Fix-bug-that-resulted-in-an-attempt-to-rename-bonds.patch
+Patch0039: 0039-azure-Fix-publishing-of-hostname.patch
 
 # Deal with noarch -> arch
 # https://bugzilla.redhat.com/show_bug.cgi?id=1067089
@@ -146,6 +152,20 @@ if [ $1 -eq 1 ] ; then
     /bin/systemctl enable cloud-final.service      >/dev/null 2>&1 || :
     /bin/systemctl enable cloud-init.service       >/dev/null 2>&1 || :
     /bin/systemctl enable cloud-init-local.service >/dev/null 2>&1 || :
+elif [ $1 -eq 2 ]; then
+    # Upgrade. If the upgrade is from a version older than 0.7.9-8,
+    # there will be stale systemd config
+    /bin/systemctl is-enabled cloud-config.service >/dev/null 2>&1 &&
+      /bin/systemctl reenable cloud-config.service >/dev/null 2>&1 || :
+
+    /bin/systemctl is-enabled cloud-final.service >/dev/null 2>&1 &&
+      /bin/systemctl reenable cloud-final.service >/dev/null 2>&1 || :
+
+    /bin/systemctl is-enabled cloud-init.service >/dev/null 2>&1 &&
+      /bin/systemctl reenable cloud-init.service >/dev/null 2>&1 || :
+
+    /bin/systemctl is-enabled cloud-init-local.service >/dev/null 2>&1 &&
+      /bin/systemctl reenable cloud-init-local.service >/dev/null 2>&1 || :
 fi
 
 %preun
@@ -191,31 +211,70 @@ fi
 %config(noreplace) %{_sysconfdir}/rsyslog.d/21-cloudinit.conf
 
 %changelog
-* Thu Feb 15 2018 Ryan McCabe <rmccabe@redhat.com> 0.7.9-9.6
-- Correctly render DNS and DOMAIN for sysconfig
-  Resolves: rhbz#1545525
+* Tue Feb 13 2018 Ryan McCabe <rmccabe@redhat.com> 0.7.9-24
+- Set DHCP_HOSTNAME on Azure to allow for the hostname to be
+  published correctly when bouncing the network.
+  Resolves: rhbz#1434109
+
+* Mon Jan 15 2018 Ryan McCabe <rmccabe@redhat.com> 0.7.9-23
+- Fix a bug tha caused cloud-init to fail as a result of trying
+  to rename bonds.
+  Resolves: rhbz#1512247
+
+* Mon Jan 15 2018 Ryan McCabe <rmccabe@redhat.com> 0.7.9-22
+- Apply patch from -21
+  Resolves: rhbz#1489270
+
+* Mon Jan 15 2018 Ryan McCabe <rmccabe@redhat.com> 0.7.9-21
+- sysconfig: Fix a potential traceback introduced in the
+  0.7.9-17 build
+  Resolves: rhbz#1489270
+
+* Sun Dec 17 2017 Ryan McCabe <rmccabe@redhat.com> 0.7.9-20
+- sysconfig: Correct rendering for dhcp on ipv6
+  Resolves: rhbz#1519271
 
-* Fri Feb 02 2018 Ryan McCabe <rmccabe@redhat.com> 0.7.9-9.5
+* Thu Nov 30 2017 Ryan McCabe <rmccabe@redhat.com> 0.7.9-19
 - sysconfig: Fix rendering of default gateway for ipv6
-  Resolves: rhbz#1540094
+  Resolves: rhbz#1492726
 
-* Tue Jan 30 2018 Ryan McCabe <rmccabe@redhat.com> 0.7.9-9.4
-- sysconfig: Fix rendering of default gateway for ipv4
-  Resolves: rhbz#1540094
-- sysconfig: Correct rendering for dhcp on ipv6
-  Resolves: rhbz#1540093
+* Fri Nov 24 2017 Ryan McCabe <rmccabe@redhat.com> 0.7.9-18
+- Start the cloud-init init local service after the dbus socket is created
+  so that the hostnamectl command works.
+  Resolves: rhbz#1450521
 
-* Mon Jan 22 2018 Ryan McCabe <rmccabe@redhat.com> 0.7.9-9.3
+* Tue Nov 21 2017 Ryan McCabe <rmccabe@redhat.com> 0.7.9-17
+- Correctly render DNS and DOMAIN for sysconfig
+  Resolves: rhbz#1489270
+
+* Mon Nov 20 2017 Ryan McCabe <rmccabe@redhat.com> 0.7.9-16
 - Disable NetworkManager management of resolv.conf if nameservers
   are specified by configuration.
-  Resolves: rhbz#1537439
+  Resolves: rhbz#1454491
+
+* Mon Nov 13 2017 Ryan McCabe <rmccabe@redhat.com> 0.7.9-15
+- Fix a null reference error in the rh_subscription module
+  Resolves: rhbz#1498974
 
-* Thu Dec 21 2017 Ryan McCabe <rmccabe@redhat.com> 0.7.9-9.2
+* Mon Nov 13 2017 Ryan McCabe <rmccabe@redhat.com> 0-7.9-14
+- Include gateway if it's included in subnet configration
+  Resolves: rhbz#1492726
+
+* Sun Nov 12 2017 Ryan McCabe <rmccabe@redhat.com> 0-7.9-13
+- Do proper cleanup of systemd units when upgrading from versions
+  0.7.9-3 through 0.7.9-8.
+  Resolves: rhbz#1465730
+
+* Thu Nov 09 2017 Ryan McCabe <rmccabe@redhat.com> 0.7.9-12
 - Prevent Azure NM and dhclient hooks from running when cloud-init is
-  disabled (rhbz#1530127)
+  disabled (rhbz#1474226)
+
+* Tue Oct 31 2017 Ryan McCabe <rmccabe@redhat.com> 0.7.9-11
+- Fix rendering of multiple static IPs per interface file
+  Resolves: rhbz#bz1497954
 
-* Tue Sep 26 2017 Ryan McCabe <rmccabe@redhat.com> 0.7.9-9.1
-- Support AliCloud datasource (rhbz#1496113)
+* Tue Sep 26 2017 Ryan McCabe <rmccabe@redhat.com> 0.7.9-10
+- AliCloud: Add support for the Alibaba Cloud datasource (rhbz#1482547)
 
 * Thu Jun 22 2017 Lars Kellogg-Stedman <lars@redhat.com> 0.7.9-9
 - RHEL/CentOS: Fix default routes for IPv4/IPv6 configuration. (rhbz#1438082)