diff --git a/addopenSUSEBase.patch b/addopenSUSEBase.patch deleted file mode 100644 index 5afa0fe..0000000 --- a/addopenSUSEBase.patch +++ /dev/null @@ -1,26 +0,0 @@ -Index: cloud-init-0.7.8/cloudinit/config/cc_resolv_conf.py -=================================================================== ---- cloud-init-0.7.8.orig/cloudinit/config/cc_resolv_conf.py -+++ cloud-init-0.7.8/cloudinit/config/cc_resolv_conf.py -@@ -58,7 +58,7 @@ LOG = logging.getLogger(__name__) - - frequency = PER_INSTANCE - --distros = ['fedora', 'rhel', 'sles'] -+distros = ['fedora', 'opensuse', 'rhel', 'sles'] - - - def generate_resolv_conf(template_fn, params, target_fname="/etc/resolv.conf"): -Index: cloud-init-0.7.8/cloudinit/distros/__init__.py -=================================================================== ---- cloud-init-0.7.8.orig/cloudinit/distros/__init__.py -+++ cloud-init-0.7.8/cloudinit/distros/__init__.py -@@ -46,7 +46,7 @@ OSFAMILIES = { - 'redhat': ['fedora', 'rhel'], - 'gentoo': ['gentoo'], - 'freebsd': ['freebsd'], -- 'suse': ['sles'], -+ 'suse': ['opensuse', 'sles'], - 'arch': ['arch'], - } - diff --git a/cloud-init-0.7.8.tar.gz b/cloud-init-0.7.8.tar.gz deleted file mode 100644 index 659c657..0000000 --- a/cloud-init-0.7.8.tar.gz +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:4a4f1f7fb9dd0987a02aa7cd6f609910294fce8f9724dcebc0cd88630b4f1fd6 -size 508777 diff --git a/cloud-init-17.1.tar.gz b/cloud-init-17.1.tar.gz new file mode 100644 index 0000000..f0becda --- /dev/null +++ b/cloud-init-17.1.tar.gz @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:80f3bf5e8f57b67ac599aba2856568aeb30bd25187c7a363bed157a1e4d63e01 +size 780532 diff --git a/cloud-init-def-config.patch b/cloud-init-def-config.patch new file mode 100644 index 0000000..c756348 --- /dev/null +++ b/cloud-init-def-config.patch @@ -0,0 +1,40 @@ +--- config/cloud.cfg.tmpl.orig ++++ config/cloud.cfg.tmpl +@@ -79,6 +79,9 @@ cloud_config_modules: + - spacewalk + - yum-add-repo + {% endif %} ++{% if variant in ["opensuse", "suse"] %} ++ - zypper_add_repo ++{% endif %} + {% if variant in ["ubuntu", "unknown", "debian"] %} + - grub-dpkg + - apt-pipelining +@@ -127,7 +130,7 @@ cloud_final_modules: + # (not accessible to handlers/transforms) + system_info: + # This will affect which distro class gets used +-{% if variant in ["centos", "debian", "fedora", "rhel", "ubuntu", "freebsd"] %} ++{% if variant in ["centos", "debian", "fedora", "opensuse", "rhel", "suse", "ubuntu", "freebsd"] %} + distro: {{ variant }} + {% else %} + # Unknown/fallback distro. +@@ -186,4 +189,18 @@ system_info: + groups: [wheel] + sudo: ["ALL=(ALL) NOPASSWD:ALL"] + shell: /bin/tcsh ++{% elif variant in ["opensuse", "suse"] %} ++ # Default user name + that default users groups (if added/used) ++ default_user: ++ name: {{ variant }} ++ lock_passwd: True ++ gecos: {{ variant }} Cloud User ++ groups: [cdrom, users] ++ sudo: ["ALL=(ALL) NOPASSWD:ALL"] ++ shell: /bin/bash ++ # Other config here will be given to the distro class and/or path classes ++ paths: ++ cloud_dir: /var/lib/cloud/ ++ templates_dir: /etc/cloud/templates/ ++ ssh_svcname: sshd + {% endif %} diff --git a/cloud-init-digital-ocean-datasource-enable-by-default.patch b/cloud-init-digital-ocean-datasource-enable-by-default.patch deleted file mode 100644 index 45e2919..0000000 --- a/cloud-init-digital-ocean-datasource-enable-by-default.patch +++ /dev/null @@ -1,23 +0,0 @@ -From 7ae201166402fbf2e6c1632028be956a954835ef Mon Sep 17 00:00:00 2001 -From: Scott Moser -Date: Tue, 18 Oct 2016 12:30:38 -0400 -Subject: DigitalOcean: enable usage of data source by default. - -Just add DigitalOcean to the list of datasources that are used -if there is no 'datasource_list' provided in config. - -diff --git a/cloudinit/settings.py b/cloudinit/settings.py -index 8c258ea..a968271 100644 ---- a/cloudinit/settings.py -+++ b/cloudinit/settings.py -@@ -32,6 +32,7 @@ CFG_BUILTIN = { - 'NoCloud', - 'ConfigDrive', - 'OpenNebula', -+ 'DigitalOcean', - 'Azure', - 'AltCloud', - 'OVF', --- -cgit v0.10.2 - diff --git a/cloud-init-digital-ocean-datasource.patch b/cloud-init-digital-ocean-datasource.patch deleted file mode 100644 index c4611ae..0000000 --- a/cloud-init-digital-ocean-datasource.patch +++ /dev/null @@ -1,782 +0,0 @@ -From 9f83bb8e80806d3dd79ba426474dc3c696e19a41 Mon Sep 17 00:00:00 2001 -From: Ben Howard -Date: Fri, 19 Aug 2016 16:28:26 -0600 -Subject: DigitalOcean: use meta-data for network configruation - -On DigitalOcean, Network information is provided via Meta-data. -It changes the datasource to be a local datasource, meaning it -will run before fallback networking is configured. - -The advantage of that is that before networking is configured it -can bring up a network device with ipv4 link-local and hit the -metadata service that lives at 169.254.169.254 to find its networking -configuration. It then takes down the link local address and lets -cloud-init configure networking. - -The configuring of a network device to go looking for a metadata -service is gated by a check of data in the smbios. This guarantees -that the code will not run on another system. - -diff --git a/cloudinit/sources/DataSourceDigitalOcean.py b/cloudinit/sources/DataSourceDigitalOcean.py -index fc596e1..c5770d5 100644 ---- a/cloudinit/sources/DataSourceDigitalOcean.py -+++ b/cloudinit/sources/DataSourceDigitalOcean.py -@@ -18,13 +18,12 @@ - # DigitalOcean Droplet API: - # https://developers.digitalocean.com/documentation/metadata/ - --import json -- - from cloudinit import log as logging - from cloudinit import sources --from cloudinit import url_helper - from cloudinit import util - -+import cloudinit.sources.helpers.digitalocean as do_helper -+ - LOG = logging.getLogger(__name__) - - BUILTIN_DS_CONFIG = { -@@ -36,11 +35,13 @@ BUILTIN_DS_CONFIG = { - MD_RETRIES = 30 - MD_TIMEOUT = 2 - MD_WAIT_RETRY = 2 -+MD_USE_IPV4LL = True - - - class DataSourceDigitalOcean(sources.DataSource): - def __init__(self, sys_cfg, distro, paths): - sources.DataSource.__init__(self, sys_cfg, distro, paths) -+ self.distro = distro - self.metadata = dict() - self.ds_cfg = util.mergemanydict([ - util.get_cfg_by_path(sys_cfg, ["datasource", "DigitalOcean"], {}), -@@ -48,80 +49,72 @@ class DataSourceDigitalOcean(sources.DataSource): - self.metadata_address = self.ds_cfg['metadata_url'] - self.retries = self.ds_cfg.get('retries', MD_RETRIES) - self.timeout = self.ds_cfg.get('timeout', MD_TIMEOUT) -+ self.use_ip4LL = self.ds_cfg.get('use_ip4LL', MD_USE_IPV4LL) - self.wait_retry = self.ds_cfg.get('wait_retry', MD_WAIT_RETRY) -+ self._network_config = None - - def _get_sysinfo(self): -- # DigitalOcean embeds vendor ID and instance/droplet_id in the -- # SMBIOS information -- -- LOG.debug("checking if instance is a DigitalOcean droplet") -- -- # Detect if we are on DigitalOcean and return the Droplet's ID -- vendor_name = util.read_dmi_data("system-manufacturer") -- if vendor_name != "DigitalOcean": -- return (False, None) -+ return do_helper.read_sysinfo() - -- LOG.info("running on DigitalOcean") -- -- droplet_id = util.read_dmi_data("system-serial-number") -- if droplet_id: -- LOG.debug(("system identified via SMBIOS as DigitalOcean Droplet" -- "{}").format(droplet_id)) -- else: -- LOG.critical(("system identified via SMBIOS as a DigitalOcean " -- "Droplet, but did not provide an ID. Please file a " -- "support ticket at: " -- "https://cloud.digitalocean.com/support/tickets/" -- "new")) -- -- return (True, droplet_id) -- -- def get_data(self, apply_filter=False): -+ def get_data(self): - (is_do, droplet_id) = self._get_sysinfo() - - # only proceed if we know we are on DigitalOcean - if not is_do: - return False - -- LOG.debug("reading metadata from {}".format(self.metadata_address)) -- response = url_helper.readurl(self.metadata_address, -- timeout=self.timeout, -- sec_between=self.wait_retry, -- retries=self.retries) -+ LOG.info("Running on digital ocean. droplet_id=%s" % droplet_id) - -- contents = util.decode_binary(response.contents) -- decoded = json.loads(contents) -+ ipv4LL_nic = None -+ if self.use_ip4LL: -+ ipv4LL_nic = do_helper.assign_ipv4_link_local() - -- self.metadata = decoded -- self.metadata['instance-id'] = decoded.get('droplet_id', droplet_id) -- self.metadata['local-hostname'] = decoded.get('hostname', droplet_id) -- self.vendordata_raw = decoded.get("vendor_data", None) -- self.userdata_raw = decoded.get("user_data", None) -- return True -+ md = do_helper.read_metadata( -+ self.metadata_address, timeout=self.timeout, -+ sec_between=self.wait_retry, retries=self.retries) - -- def get_public_ssh_keys(self): -- public_keys = self.metadata.get('public_keys', []) -- if isinstance(public_keys, list): -- return public_keys -- else: -- return [public_keys] -+ self.metadata_full = md -+ self.metadata['instance-id'] = md.get('droplet_id', droplet_id) -+ self.metadata['local-hostname'] = md.get('hostname', droplet_id) -+ self.metadata['interfaces'] = md.get('interfaces') -+ self.metadata['public-keys'] = md.get('public_keys') -+ self.metadata['availability_zone'] = md.get('region', 'default') -+ self.vendordata_raw = md.get("vendor_data", None) -+ self.userdata_raw = md.get("user_data", None) - -- @property -- def availability_zone(self): -- return self.metadata.get('region', 'default') -+ if ipv4LL_nic: -+ do_helper.del_ipv4_link_local(ipv4LL_nic) - -- @property -- def launch_index(self): -- return None -+ return True - - def check_instance_id(self, sys_cfg): - return sources.instance_id_matches_system_uuid( - self.get_instance_id(), 'system-serial-number') - -+ @property -+ def network_config(self): -+ """Configure the networking. This needs to be done each boot, since -+ the IP information may have changed due to snapshot and/or -+ migration. -+ """ -+ -+ if self._network_config: -+ return self._network_config -+ -+ interfaces = self.metadata.get('interfaces') -+ LOG.debug(interfaces) -+ if not interfaces: -+ raise Exception("Unable to get meta-data from server....") -+ -+ nameservers = self.metadata_full['dns']['nameservers'] -+ self._network_config = do_helper.convert_network_configuration( -+ interfaces, nameservers) -+ return self._network_config -+ - - # Used to match classes to dependencies - datasources = [ -- (DataSourceDigitalOcean, (sources.DEP_FILESYSTEM, sources.DEP_NETWORK)), -+ (DataSourceDigitalOcean, (sources.DEP_FILESYSTEM, )), - ] - - -diff --git a/cloudinit/sources/helpers/digitalocean.py b/cloudinit/sources/helpers/digitalocean.py -new file mode 100644 -index 0000000..b0a721c ---- /dev/null -+++ b/cloudinit/sources/helpers/digitalocean.py -@@ -0,0 +1,218 @@ -+# vi: ts=4 expandtab -+# -+# Author: Ben Howard -+ -+# This program is free software: you can redistribute it and/or modify -+# it under the terms of the GNU General Public License version 3, as -+# published by the Free Software Foundation. -+# -+# This program is distributed in the hope that it will be useful, -+# but WITHOUT ANY WARRANTY; without even the implied warranty of -+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -+# GNU General Public License for more details. -+# -+# You should have received a copy of the GNU General Public License -+# along with this program. If not, see . -+ -+import json -+import random -+ -+from cloudinit import log as logging -+from cloudinit import net as cloudnet -+from cloudinit import url_helper -+from cloudinit import util -+ -+NIC_MAP = {'public': 'eth0', 'private': 'eth1'} -+ -+LOG = logging.getLogger(__name__) -+ -+ -+def assign_ipv4_link_local(nic=None): -+ """Bring up NIC using an address using link-local (ip4LL) IPs. On -+ DigitalOcean, the link-local domain is per-droplet routed, so there -+ is no risk of collisions. However, to be more safe, the ip4LL -+ address is random. -+ """ -+ -+ if not nic: -+ for cdev in sorted(cloudnet.get_devicelist()): -+ if cloudnet.is_physical(cdev): -+ nic = cdev -+ LOG.debug("assigned nic '%s' for link-local discovery", nic) -+ break -+ -+ if not nic: -+ raise RuntimeError("unable to find interfaces to access the" -+ "meta-data server. This droplet is broken.") -+ -+ addr = "169.254.{0}.{1}/16".format(random.randint(1, 168), -+ random.randint(0, 255)) -+ -+ ip_addr_cmd = ['ip', 'addr', 'add', addr, 'dev', nic] -+ ip_link_cmd = ['ip', 'link', 'set', 'dev', nic, 'up'] -+ -+ if not util.which('ip'): -+ raise RuntimeError("No 'ip' command available to configure ip4LL " -+ "address") -+ -+ try: -+ (result, _err) = util.subp(ip_addr_cmd) -+ LOG.debug("assigned ip4LL address '%s' to '%s'", addr, nic) -+ -+ (result, _err) = util.subp(ip_link_cmd) -+ LOG.debug("brought device '%s' up", nic) -+ except Exception: -+ util.logexc(LOG, "ip4LL address assignment of '%s' to '%s' failed." -+ " Droplet networking will be broken", addr, nic) -+ raise -+ -+ return nic -+ -+ -+def del_ipv4_link_local(nic=None): -+ """Remove the ip4LL address. While this is not necessary, the ip4LL -+ address is extraneous and confusing to users. -+ """ -+ if not nic: -+ LOG.debug("no link_local address interface defined, skipping link " -+ "local address cleanup") -+ return -+ -+ LOG.debug("cleaning up ipv4LL address") -+ -+ ip_addr_cmd = ['ip', 'addr', 'flush', 'dev', nic] -+ -+ try: -+ (result, _err) = util.subp(ip_addr_cmd) -+ LOG.debug("removed ip4LL addresses from %s", nic) -+ -+ except Exception as e: -+ util.logexc(LOG, "failed to remove ip4LL address from '%s'.", nic, e) -+ -+ -+def convert_network_configuration(config, dns_servers): -+ """Convert the DigitalOcean Network description into Cloud-init's netconfig -+ format. -+ -+ Example JSON: -+ {'public': [ -+ {'mac': '04:01:58:27:7f:01', -+ 'ipv4': {'gateway': '45.55.32.1', -+ 'netmask': '255.255.224.0', -+ 'ip_address': '45.55.50.93'}, -+ 'anchor_ipv4': { -+ 'gateway': '10.17.0.1', -+ 'netmask': '255.255.0.0', -+ 'ip_address': '10.17.0.9'}, -+ 'type': 'public', -+ 'ipv6': {'gateway': '....', -+ 'ip_address': '....', -+ 'cidr': 64}} -+ ], -+ 'private': [ -+ {'mac': '04:01:58:27:7f:02', -+ 'ipv4': {'gateway': '10.132.0.1', -+ 'netmask': '255.255.0.0', -+ 'ip_address': '10.132.75.35'}, -+ 'type': 'private'} -+ ] -+ } -+ """ -+ -+ def _get_subnet_part(pcfg, nameservers=None): -+ subpart = {'type': 'static', -+ 'control': 'auto', -+ 'address': pcfg.get('ip_address'), -+ 'gateway': pcfg.get('gateway')} -+ -+ if nameservers: -+ subpart['dns_nameservers'] = nameservers -+ -+ if ":" in pcfg.get('ip_address'): -+ subpart['address'] = "{0}/{1}".format(pcfg.get('ip_address'), -+ pcfg.get('cidr')) -+ else: -+ subpart['netmask'] = pcfg.get('netmask') -+ -+ return subpart -+ -+ all_nics = [] -+ for k in ('public', 'private'): -+ if k in config: -+ all_nics.extend(config[k]) -+ -+ macs_to_nics = cloudnet.get_interfaces_by_mac() -+ nic_configs = [] -+ -+ for nic in all_nics: -+ -+ mac_address = nic.get('mac') -+ sysfs_name = macs_to_nics.get(mac_address) -+ nic_type = nic.get('type', 'unknown') -+ # Note: the entry 'public' above contains a list, but -+ # the list will only ever have one nic inside it per digital ocean. -+ # If it ever had more than one nic, then this code would -+ # assign all 'public' the same name. -+ if_name = NIC_MAP.get(nic_type, sysfs_name) -+ -+ LOG.debug("mapped %s interface to %s, assigning name of %s", -+ mac_address, sysfs_name, if_name) -+ -+ ncfg = {'type': 'physical', -+ 'mac_address': mac_address, -+ 'name': if_name} -+ -+ subnets = [] -+ for netdef in ('ipv4', 'ipv6', 'anchor_ipv4', 'anchor_ipv6'): -+ raw_subnet = nic.get(netdef, None) -+ if not raw_subnet: -+ continue -+ -+ sub_part = _get_subnet_part(raw_subnet) -+ if nic_type == 'public' and 'anchor' not in netdef: -+ # add DNS resolvers to the public interfaces only -+ sub_part = _get_subnet_part(raw_subnet, dns_servers) -+ else: -+ # remove the gateway any non-public interfaces -+ if 'gateway' in sub_part: -+ del sub_part['gateway'] -+ -+ subnets.append(sub_part) -+ -+ ncfg['subnets'] = subnets -+ nic_configs.append(ncfg) -+ LOG.debug("nic '%s' configuration: %s", if_name, ncfg) -+ -+ return {'version': 1, 'config': nic_configs} -+ -+ -+def read_metadata(url, timeout=2, sec_between=2, retries=30): -+ response = url_helper.readurl(url, timeout=timeout, -+ sec_between=sec_between, retries=retries) -+ if not response.ok(): -+ raise RuntimeError("unable to read metadata at %s" % url) -+ return json.loads(response.contents.decode()) -+ -+ -+def read_sysinfo(): -+ # DigitalOcean embeds vendor ID and instance/droplet_id in the -+ # SMBIOS information -+ -+ # Detect if we are on DigitalOcean and return the Droplet's ID -+ vendor_name = util.read_dmi_data("system-manufacturer") -+ if vendor_name != "DigitalOcean": -+ return (False, None) -+ -+ droplet_id = util.read_dmi_data("system-serial-number") -+ if droplet_id: -+ LOG.debug("system identified via SMBIOS as DigitalOcean Droplet: %s", -+ droplet_id) -+ else: -+ msg = ("system identified via SMBIOS as a DigitalOcean " -+ "Droplet, but did not provide an ID. Please file a " -+ "support ticket at: " -+ "https://cloud.digitalocean.com/support/tickets/new") -+ LOG.critical(msg) -+ raise RuntimeError(msg) -+ -+ return (True, droplet_id) -diff --git a/tests/unittests/test_datasource/test_digitalocean.py b/tests/unittests/test_datasource/test_digitalocean.py -index f5d2ef3..bdfe0ba 100644 ---- a/tests/unittests/test_datasource/test_digitalocean.py -+++ b/tests/unittests/test_datasource/test_digitalocean.py -@@ -20,25 +20,123 @@ import json - from cloudinit import helpers - from cloudinit import settings - from cloudinit.sources import DataSourceDigitalOcean -+from cloudinit.sources.helpers import digitalocean - --from .. import helpers as test_helpers --from ..helpers import HttprettyTestCase -- --httpretty = test_helpers.import_httpretty() -+from ..helpers import mock, TestCase - - DO_MULTIPLE_KEYS = ["ssh-rsa AAAAB3NzaC1yc2EAAAA... test1@do.co", - "ssh-rsa AAAAB3NzaC1yc2EAAAA... test2@do.co"] - DO_SINGLE_KEY = "ssh-rsa AAAAB3NzaC1yc2EAAAA... test@do.co" - --DO_META = { -- 'user_data': 'user_data_here', -- 'vendor_data': 'vendor_data_here', -- 'public_keys': DO_SINGLE_KEY, -- 'region': 'nyc3', -- 'id': '2000000', -- 'hostname': 'cloudinit-test', -+# the following JSON was taken from droplet (that's why its a string) -+DO_META = json.loads(""" -+{ -+ "droplet_id": "22532410", -+ "hostname": "utl-96268", -+ "vendor_data": "vendordata goes here", -+ "user_data": "userdata goes here", -+ "public_keys": "", -+ "auth_key": "authorization_key", -+ "region": "nyc3", -+ "interfaces": { -+ "private": [ -+ { -+ "ipv4": { -+ "ip_address": "10.132.6.205", -+ "netmask": "255.255.0.0", -+ "gateway": "10.132.0.1" -+ }, -+ "mac": "04:01:57:d1:9e:02", -+ "type": "private" -+ } -+ ], -+ "public": [ -+ { -+ "ipv4": { -+ "ip_address": "192.0.0.20", -+ "netmask": "255.255.255.0", -+ "gateway": "104.236.0.1" -+ }, -+ "ipv6": { -+ "ip_address": "2604:A880:0800:0000:1000:0000:0000:0000", -+ "cidr": 64, -+ "gateway": "2604:A880:0800:0000:0000:0000:0000:0001" -+ }, -+ "anchor_ipv4": { -+ "ip_address": "10.0.0.5", -+ "netmask": "255.255.0.0", -+ "gateway": "10.0.0.1" -+ }, -+ "mac": "04:01:57:d1:9e:01", -+ "type": "public" -+ } -+ ] -+ }, -+ "floating_ip": { -+ "ipv4": { -+ "active": false -+ } -+ }, -+ "dns": { -+ "nameservers": [ -+ "2001:4860:4860::8844", -+ "2001:4860:4860::8888", -+ "8.8.8.8" -+ ] -+ } -+} -+""") -+ -+# This has no private interface -+DO_META_2 = { -+ "droplet_id": 27223699, -+ "hostname": "smtest1", -+ "vendor_data": "\n".join([ -+ ('"Content-Type: multipart/mixed; ' -+ 'boundary=\"===============8645434374073493512==\"'), -+ 'MIME-Version: 1.0', -+ '', -+ '--===============8645434374073493512==', -+ 'MIME-Version: 1.0' -+ 'Content-Type: text/cloud-config; charset="us-ascii"' -+ 'Content-Transfer-Encoding: 7bit' -+ 'Content-Disposition: attachment; filename="cloud-config"' -+ '', -+ '#cloud-config', -+ 'disable_root: false', -+ 'manage_etc_hosts: true', -+ '', -+ '', -+ '--===============8645434374073493512==' -+ ]), -+ "public_keys": [ -+ "ssh-rsa AAAAB3NzaN...N3NtHw== smoser@brickies" -+ ], -+ "auth_key": "88888888888888888888888888888888", -+ "region": "nyc3", -+ "interfaces": { -+ "public": [{ -+ "ipv4": { -+ "ip_address": "45.55.249.133", -+ "netmask": "255.255.192.0", -+ "gateway": "45.55.192.1" -+ }, -+ "anchor_ipv4": { -+ "ip_address": "10.17.0.5", -+ "netmask": "255.255.0.0", -+ "gateway": "10.17.0.1" -+ }, -+ "mac": "ae:cc:08:7c:88:00", -+ "type": "public" -+ }] -+ }, -+ "floating_ip": {"ipv4": {"active": True, "ip_address": "138.197.59.92"}}, -+ "dns": {"nameservers": ["8.8.8.8", "8.8.4.4"]}, -+ "tags": None, - } - -+DO_META['public_keys'] = DO_SINGLE_KEY -+ - MD_URL = 'http://169.254.169.254/metadata/v1.json' - - -@@ -46,69 +144,189 @@ def _mock_dmi(): - return (True, DO_META.get('id')) - - --def _request_callback(method, uri, headers): -- return (200, headers, json.dumps(DO_META)) -- -- --class TestDataSourceDigitalOcean(HttprettyTestCase): -+class TestDataSourceDigitalOcean(TestCase): - """ - Test reading the meta-data - """ - -- def setUp(self): -- self.ds = DataSourceDigitalOcean.DataSourceDigitalOcean( -- settings.CFG_BUILTIN, None, -- helpers.Paths({})) -- self.ds._get_sysinfo = _mock_dmi -- super(TestDataSourceDigitalOcean, self).setUp() -- -- @httpretty.activate -- def test_connection(self): -- httpretty.register_uri( -- httpretty.GET, MD_URL, -- body=json.dumps(DO_META)) -- -- success = self.ds.get_data() -- self.assertTrue(success) -- -- @httpretty.activate -- def test_metadata(self): -- httpretty.register_uri( -- httpretty.GET, MD_URL, -- body=_request_callback) -- self.ds.get_data() -+ def get_ds(self, get_sysinfo=_mock_dmi): -+ ds = DataSourceDigitalOcean.DataSourceDigitalOcean( -+ settings.CFG_BUILTIN, None, helpers.Paths({})) -+ ds.use_ip4LL = False -+ if get_sysinfo is not None: -+ ds._get_sysinfo = get_sysinfo -+ return ds - -- self.assertEqual(DO_META.get('user_data'), -- self.ds.get_userdata_raw()) -+ @mock.patch('cloudinit.sources.helpers.digitalocean.read_sysinfo') -+ def test_returns_false_not_on_docean(self, m_read_sysinfo): -+ m_read_sysinfo.return_value = (False, None) -+ ds = self.get_ds(get_sysinfo=None) -+ self.assertEqual(False, ds.get_data()) -+ m_read_sysinfo.assert_called() - -- self.assertEqual(DO_META.get('vendor_data'), -- self.ds.get_vendordata_raw()) -+ @mock.patch('cloudinit.sources.helpers.digitalocean.read_metadata') -+ def test_metadata(self, mock_readmd): -+ mock_readmd.return_value = DO_META.copy() - -- self.assertEqual(DO_META.get('region'), -- self.ds.availability_zone) -+ ds = self.get_ds() -+ ret = ds.get_data() -+ self.assertTrue(ret) - -- self.assertEqual(DO_META.get('id'), -- self.ds.get_instance_id()) -+ mock_readmd.assert_called() - -- self.assertEqual(DO_META.get('hostname'), -- self.ds.get_hostname()) -+ self.assertEqual(DO_META.get('user_data'), ds.get_userdata_raw()) -+ self.assertEqual(DO_META.get('vendor_data'), ds.get_vendordata_raw()) -+ self.assertEqual(DO_META.get('region'), ds.availability_zone) -+ self.assertEqual(DO_META.get('droplet_id'), ds.get_instance_id()) -+ self.assertEqual(DO_META.get('hostname'), ds.get_hostname()) - - # Single key - self.assertEqual([DO_META.get('public_keys')], -- self.ds.get_public_ssh_keys()) -+ ds.get_public_ssh_keys()) - -- self.assertIsInstance(self.ds.get_public_ssh_keys(), list) -+ self.assertIsInstance(ds.get_public_ssh_keys(), list) - -- @httpretty.activate -- def test_multiple_ssh_keys(self): -- DO_META['public_keys'] = DO_MULTIPLE_KEYS -- httpretty.register_uri( -- httpretty.GET, MD_URL, -- body=_request_callback) -- self.ds.get_data() -+ @mock.patch('cloudinit.sources.helpers.digitalocean.read_metadata') -+ def test_multiple_ssh_keys(self, mock_readmd): -+ metadata = DO_META.copy() -+ metadata['public_keys'] = DO_MULTIPLE_KEYS -+ mock_readmd.return_value = metadata.copy() -+ -+ ds = self.get_ds() -+ ret = ds.get_data() -+ self.assertTrue(ret) -+ -+ mock_readmd.assert_called() - - # Multiple keys -- self.assertEqual(DO_META.get('public_keys'), -- self.ds.get_public_ssh_keys()) -+ self.assertEqual(metadata['public_keys'], ds.get_public_ssh_keys()) -+ self.assertIsInstance(ds.get_public_ssh_keys(), list) -+ -+ -+class TestNetworkConvert(TestCase): -+ -+ def _get_networking(self): -+ netcfg = digitalocean.convert_network_configuration( -+ DO_META['interfaces'], DO_META['dns']['nameservers']) -+ self.assertIn('config', netcfg) -+ return netcfg -+ -+ def test_networking_defined(self): -+ netcfg = self._get_networking() -+ self.assertIsNotNone(netcfg) -+ -+ for nic_def in netcfg.get('config'): -+ print(json.dumps(nic_def, indent=3)) -+ n_type = nic_def.get('type') -+ n_subnets = nic_def.get('type') -+ n_name = nic_def.get('name') -+ n_mac = nic_def.get('mac_address') -+ -+ self.assertIsNotNone(n_type) -+ self.assertIsNotNone(n_subnets) -+ self.assertIsNotNone(n_name) -+ self.assertIsNotNone(n_mac) -+ -+ def _get_nic_definition(self, int_type, expected_name): -+ """helper function to return if_type (i.e. public) and the expected -+ name used by cloud-init (i.e eth0)""" -+ netcfg = self._get_networking() -+ meta_def = (DO_META.get('interfaces')).get(int_type)[0] -+ -+ self.assertEqual(int_type, meta_def.get('type')) -+ -+ for nic_def in netcfg.get('config'): -+ print(nic_def) -+ if nic_def.get('name') == expected_name: -+ return nic_def, meta_def -+ -+ def _get_match_subn(self, subnets, ip_addr): -+ """get the matching subnet definition based on ip address""" -+ for subn in subnets: -+ address = subn.get('address') -+ self.assertIsNotNone(address) -+ -+ # equals won't work because of ipv6 addressing being in -+ # cidr notation, i.e fe00::1/64 -+ if ip_addr in address: -+ print(json.dumps(subn, indent=3)) -+ return subn -+ -+ def test_public_interface_defined(self): -+ """test that the public interface is defined as eth0""" -+ (nic_def, meta_def) = self._get_nic_definition('public', 'eth0') -+ self.assertEqual('eth0', nic_def.get('name')) -+ self.assertEqual(meta_def.get('mac'), nic_def.get('mac_address')) -+ self.assertEqual('physical', nic_def.get('type')) -+ -+ def test_private_interface_defined(self): -+ """test that the private interface is defined as eth1""" -+ (nic_def, meta_def) = self._get_nic_definition('private', 'eth1') -+ self.assertEqual('eth1', nic_def.get('name')) -+ self.assertEqual(meta_def.get('mac'), nic_def.get('mac_address')) -+ self.assertEqual('physical', nic_def.get('type')) -+ -+ def _check_dns_nameservers(self, subn_def): -+ self.assertIn('dns_nameservers', subn_def) -+ expected_nameservers = DO_META['dns']['nameservers'] -+ nic_nameservers = subn_def.get('dns_nameservers') -+ self.assertEqual(expected_nameservers, nic_nameservers) -+ -+ def test_public_interface_ipv6(self): -+ """test public ipv6 addressing""" -+ (nic_def, meta_def) = self._get_nic_definition('public', 'eth0') -+ ipv6_def = meta_def.get('ipv6') -+ self.assertIsNotNone(ipv6_def) -+ -+ subn_def = self._get_match_subn(nic_def.get('subnets'), -+ ipv6_def.get('ip_address')) -+ -+ cidr_notated_address = "{0}/{1}".format(ipv6_def.get('ip_address'), -+ ipv6_def.get('cidr')) -+ -+ self.assertEqual(cidr_notated_address, subn_def.get('address')) -+ self.assertEqual(ipv6_def.get('gateway'), subn_def.get('gateway')) -+ self._check_dns_nameservers(subn_def) -+ -+ def test_public_interface_ipv4(self): -+ """test public ipv4 addressing""" -+ (nic_def, meta_def) = self._get_nic_definition('public', 'eth0') -+ ipv4_def = meta_def.get('ipv4') -+ self.assertIsNotNone(ipv4_def) -+ -+ subn_def = self._get_match_subn(nic_def.get('subnets'), -+ ipv4_def.get('ip_address')) -+ -+ self.assertEqual(ipv4_def.get('netmask'), subn_def.get('netmask')) -+ self.assertEqual(ipv4_def.get('gateway'), subn_def.get('gateway')) -+ self._check_dns_nameservers(subn_def) -+ -+ def test_public_interface_anchor_ipv4(self): -+ """test public ipv4 addressing""" -+ (nic_def, meta_def) = self._get_nic_definition('public', 'eth0') -+ ipv4_def = meta_def.get('anchor_ipv4') -+ self.assertIsNotNone(ipv4_def) -+ -+ subn_def = self._get_match_subn(nic_def.get('subnets'), -+ ipv4_def.get('ip_address')) -+ -+ self.assertEqual(ipv4_def.get('netmask'), subn_def.get('netmask')) -+ self.assertNotIn('gateway', subn_def) -+ -+ def test_convert_without_private(self): -+ netcfg = digitalocean.convert_network_configuration( -+ DO_META_2['interfaces'], DO_META_2['dns']['nameservers']) - -- self.assertIsInstance(self.ds.get_public_ssh_keys(), list) -+ byname = {} -+ for i in netcfg['config']: -+ if 'name' in i: -+ if i['name'] in byname: -+ raise ValueError("name '%s' in config twice: %s" % -+ (i['name'], netcfg)) -+ byname[i['name']] = i -+ self.assertTrue('eth0' in byname) -+ self.assertTrue('subnets' in byname['eth0']) -+ eth0 = byname['eth0'] -+ self.assertEqual( -+ sorted(['45.55.249.133', '10.17.0.5']), -+ sorted([i['address'] for i in eth0['subnets']])) --- -cgit v0.10.2 - diff --git a/cloud-init-final-no-apt.patch b/cloud-init-final-no-apt.patch new file mode 100644 index 0000000..20cc383 --- /dev/null +++ b/cloud-init-final-no-apt.patch @@ -0,0 +1,15 @@ +Index: cloud-init-17.1/systemd/cloud-final.service.tmpl +=================================================================== +--- cloud-init-17.1.orig/systemd/cloud-final.service.tmpl ++++ cloud-init-17.1/systemd/cloud-final.service.tmpl +@@ -4,9 +4,9 @@ Description=Execute cloud user/final scr + After=network-online.target cloud-config.service rc-local.service + {% if variant in ["ubuntu", "unknown", "debian"] %} + After=multi-user.target ++Before=apt-daily.service + {% endif %} + Wants=network-online.target cloud-config.service +-Before=apt-daily.service + + [Service] + Type=oneshot diff --git a/cloud-init-finalbeforelogin.patch b/cloud-init-finalbeforelogin.patch deleted file mode 100644 index ce5c247..0000000 --- a/cloud-init-finalbeforelogin.patch +++ /dev/null @@ -1,20 +0,0 @@ -Index: systemd/cloud-final.service -=================================================================== ---- systemd/cloud-final.service.orig -+++ systemd/cloud-final.service -@@ -1,6 +1,7 @@ - [Unit] - Description=Execute cloud user/final scripts - After=network-online.target cloud-config.service rc-local.service multi-user.target -+Before=systemd-logind.service - Wants=network-online.target cloud-config.service - - [Service] -@@ -9,6 +10,7 @@ ExecStart=/usr/bin/cloud-init modules -- - RemainAfterExit=yes - TimeoutSec=0 - KillMode=process -+TasksMax=infinity - - # Output needs to appear in instance console output - StandardOutput=journal+console diff --git a/cloud-init-fix-unicode-handling-binarydecode.patch b/cloud-init-fix-unicode-handling-binarydecode.patch deleted file mode 100644 index e8c486d..0000000 --- a/cloud-init-fix-unicode-handling-binarydecode.patch +++ /dev/null @@ -1,13 +0,0 @@ -Index: cloudinit/util.py -=================================================================== ---- cloudinit/util.py.orig -+++ cloudinit/util.py -@@ -155,7 +155,7 @@ def target_path(target, path=None): - - def decode_binary(blob, encoding='utf-8'): - # Converts a binary type into a text type using given encoding. -- if isinstance(blob, six.text_type): -+ if isinstance(blob, six.string_types): - return blob - return blob.decode(encoding) - diff --git a/cloud-init-handle-no-carrier.patch b/cloud-init-handle-no-carrier.patch deleted file mode 100644 index 7727a72..0000000 --- a/cloud-init-handle-no-carrier.patch +++ /dev/null @@ -1,210 +0,0 @@ ---- cloudinit/net/__init__.py.orig -+++ cloudinit/net/__init__.py -@@ -33,10 +33,12 @@ def sys_dev_path(devname, path=""): - - - def read_sys_net(devname, path, translate=None, enoent=None, keyerror=None): -+ dev_path = sys_dev_path(devname, path) - try: -- contents = util.load_file(sys_dev_path(devname, path)) -+ contents = util.load_file(dev_path) - except (OSError, IOError) as e: -- if getattr(e, 'errno', None) in (errno.ENOENT, errno.ENOTDIR): -+ e_errno = getattr(e, 'errno', None) -+ if e_errno in (errno.ENOENT, errno.ENOTDIR): - if enoent is not None: - return enoent - raise -@@ -109,24 +111,9 @@ def is_disabled_cfg(cfg): - return cfg.get('config') == "disabled" - - --def sys_netdev_info(name, field): -- if not os.path.exists(os.path.join(SYS_CLASS_NET, name)): -- raise OSError("%s: interface does not exist in %s" % -- (name, SYS_CLASS_NET)) -- fname = os.path.join(SYS_CLASS_NET, name, field) -- if not os.path.exists(fname): -- raise OSError("%s: could not find sysfs entry: %s" % (name, fname)) -- data = util.load_file(fname) -- if data[-1] == '\n': -- data = data[:-1] -- return data -- -- - def generate_fallback_config(): - """Determine which attached net dev is most likely to have a connection and - generate network state to run dhcp on that interface""" -- # by default use eth0 as primary interface -- nconf = {'config': [], 'version': 1} - - # get list of interfaces that could have connections - invalid_interfaces = set(['lo']) -@@ -143,28 +130,30 @@ def generate_fallback_config(): - # skip any bridges - continue - try: -- carrier = int(sys_netdev_info(interface, 'carrier')) -+ carrier = read_sys_net(interface, 'carrier', enoent=False) - if carrier: -+ carrier = int(carrier) - connected.append(interface) - continue -- except OSError: -+ except (IOError, OSError, TypeError): - pass - # check if nic is dormant or down, as this may make a nick appear to - # not have a carrier even though it could acquire one when brought - # online by dhclient - try: -- dormant = int(sys_netdev_info(interface, 'dormant')) -+ dormant = read_sys_net(interface, 'dormant', enoent=False) - if dormant: -+ domant = int(dormant) - possibly_connected.append(interface) - continue -- except OSError: -+ except (IOError, OSError, TypeError): - pass - try: -- operstate = sys_netdev_info(interface, 'operstate') -+ operstate = read_sys_net(interface, 'operstate', enoent=False) - if operstate in ['dormant', 'down', 'lowerlayerdown', 'unknown']: - possibly_connected.append(interface) - continue -- except OSError: -+ except (IOError, OSError): - pass - - # don't bother with interfaces that might not be connected if there are -@@ -173,23 +162,29 @@ def generate_fallback_config(): - potential_interfaces = connected - else: - potential_interfaces = possibly_connected -- # if there are no interfaces, give up -- if not potential_interfaces: -- return -+ - # if eth0 exists use it above anything else, otherwise get the interface -- # that looks 'first' -- if DEFAULT_PRIMARY_INTERFACE in potential_interfaces: -- name = DEFAULT_PRIMARY_INTERFACE -+ # that we can read 'first' (using the sorted defintion of first). -+ names = [DEFAULT_PRIMARY_INTERFACE] -+ names.extend(sorted(potential_interfaces)) -+ target_name = None -+ target_mac = None -+ for name in names: -+ if name not in potential_interfaces: -+ continue -+ mac = read_sys_net(name, 'address', enoent=False) -+ if mac: -+ target_name = name -+ target_mac = mac -+ break -+ if target_mac and target_name: -+ nconf = {'config': [], 'version': 1} -+ nconf['config'].append( -+ {'type': 'physical', 'name': target_name, -+ 'mac_address': target_mac, 'subnets': [{'type': 'dhcp'}]}) -+ return nconf - else: -- name = sorted(potential_interfaces)[0] -- -- mac = sys_netdev_info(name, 'address') -- target_name = name -- -- nconf['config'].append( -- {'type': 'physical', 'name': target_name, -- 'mac_address': mac, 'subnets': [{'type': 'dhcp'}]}) -- return nconf -+ return None - - - def apply_network_config_names(netcfg, strict_present=True, strict_busy=True): ---- cloudinit/net/cmdline.py.orig -+++ cloudinit/net/cmdline.py -@@ -26,7 +26,7 @@ import sys - import six - - from . import get_devicelist --from . import sys_netdev_info -+from . import read_sys_net - - from cloudinit import util - -@@ -197,7 +197,10 @@ def read_kernel_cmdline_config(files=Non - return None - - if mac_addrs is None: -- mac_addrs = dict((k, sys_netdev_info(k, 'address')) -- for k in get_devicelist()) -+ mac_addrs = {} -+ for k in get_devicelist(): -+ mac_addr = read_sys_net(k, 'address', enoent=False) -+ if mac_addr: -+ mac_addrs[k] = mac_addr - - return config_from_klibc_net_cfg(files=files, mac_addrs=mac_addrs) ---- tests/unittests/test_net.py.orig -+++ tests/unittests/test_net.py -@@ -422,7 +422,7 @@ pre-down route del -net 10.0.0.0 netmask - } - - --def _setup_test(tmp_dir, mock_get_devicelist, mock_sys_netdev_info, -+def _setup_test(tmp_dir, mock_get_devicelist, mock_read_sys_net, - mock_sys_dev_path): - mock_get_devicelist.return_value = ['eth1000'] - dev_characteristics = { -@@ -435,10 +435,10 @@ def _setup_test(tmp_dir, mock_get_device - } - } - -- def netdev_info(name, field): -+ def fake_read(devname, path, translate=None, enoent=None, keyerror=None): - return dev_characteristics[name][field] - -- mock_sys_netdev_info.side_effect = netdev_info -+ mock_read_sys_net.side_effect = fake_read - - def sys_dev_path(devname, path=""): - return tmp_dir + devname + "/" + path -@@ -454,15 +454,15 @@ def _setup_test(tmp_dir, mock_get_device - class TestSysConfigRendering(TestCase): - - @mock.patch("cloudinit.net.sys_dev_path") -- @mock.patch("cloudinit.net.sys_netdev_info") -+ @mock.patch("cloudinit.net.read_sys_net") - @mock.patch("cloudinit.net.get_devicelist") - def test_default_generation(self, mock_get_devicelist, -- mock_sys_netdev_info, -+ mock_read_sys_net, - mock_sys_dev_path): - tmp_dir = tempfile.mkdtemp() - self.addCleanup(shutil.rmtree, tmp_dir) - _setup_test(tmp_dir, mock_get_devicelist, -- mock_sys_netdev_info, mock_sys_dev_path) -+ mock_read_sys_net, mock_sys_dev_path) - - network_cfg = net.generate_fallback_config() - ns = network_state.parse_net_config_data(network_cfg, -@@ -511,15 +511,15 @@ USERCTL=no - class TestEniNetRendering(TestCase): - - @mock.patch("cloudinit.net.sys_dev_path") -- @mock.patch("cloudinit.net.sys_netdev_info") -+ @mock.patch("cloudinit.net.read_sys_net") - @mock.patch("cloudinit.net.get_devicelist") - def test_default_generation(self, mock_get_devicelist, -- mock_sys_netdev_info, -+ mock_read_sys_net, - mock_sys_dev_path): - tmp_dir = tempfile.mkdtemp() - self.addCleanup(shutil.rmtree, tmp_dir) - _setup_test(tmp_dir, mock_get_devicelist, -- mock_sys_netdev_info, mock_sys_dev_path) -+ mock_read_sys_net, mock_sys_dev_path) - - network_cfg = net.generate_fallback_config() - ns = network_state.parse_net_config_data(network_cfg, diff --git a/cloud-init-handle-not-implemented-query.patch b/cloud-init-handle-not-implemented-query.patch deleted file mode 100644 index 6e72ee9..0000000 --- a/cloud-init-handle-not-implemented-query.patch +++ /dev/null @@ -1,13 +0,0 @@ ---- cloudinit/cmd/main.py.orig -+++ cloudinit/cmd/main.py -@@ -681,6 +681,10 @@ def main(sysv_args=None): - rname, rdesc = ("dhclient-hook", - "running dhclient-hook module") - -+ elif name == 'query': -+ print('Action query is not yet implemented') -+ sys.exit(1) -+ - args.reporter = events.ReportEventStack( - rname, rdesc, reporting_enabled=report_on) - diff --git a/cloud-init-more-tasks.patch b/cloud-init-more-tasks.patch new file mode 100644 index 0000000..f2bd64f --- /dev/null +++ b/cloud-init-more-tasks.patch @@ -0,0 +1,10 @@ +--- systemd/cloud-final.service.tmpl.orig ++++ systemd/cloud-final.service.tmpl +@@ -14,6 +14,7 @@ ExecStart=/usr/bin/cloud-init modules -- + RemainAfterExit=yes + TimeoutSec=0 + KillMode=process ++TasksMax=infinity + + # Output needs to appear in instance console output + StandardOutput=journal+console diff --git a/cloud-init-net-eni.patch b/cloud-init-net-eni.patch deleted file mode 100644 index f8cbea6..0000000 --- a/cloud-init-net-eni.patch +++ /dev/null @@ -1,18 +0,0 @@ ---- cloudinit/net/eni.py.orig -+++ cloudinit/net/eni.py -@@ -338,6 +338,7 @@ class Renderer(renderer.Renderer): - up = indent + "post-up route add" - down = indent + "pre-down route del" - or_true = " || true" -+ gateway = indent + "gateway " - mapping = { - 'network': '-net', - 'netmask': 'netmask', -@@ -346,6 +347,7 @@ class Renderer(renderer.Renderer): - } - if route['network'] == '0.0.0.0' and route['netmask'] == '0.0.0.0': - default_gw = " default gw %s" % route['gateway'] -+ content.append(gateway + route['gateway']) - content.append(up + default_gw + or_true) - content.append(down + default_gw + or_true) - elif route['network'] == '::' and route['netmask'] == 0: diff --git a/cloud-init-net-sysconfig-lp1665441.patch b/cloud-init-net-sysconfig-lp1665441.patch deleted file mode 100644 index 55fc87f..0000000 --- a/cloud-init-net-sysconfig-lp1665441.patch +++ /dev/null @@ -1,15 +0,0 @@ ---- cloud-init-0.7.8.orig/cloudinit/net/sysconfig.py -+++ cloud-init-0.7.8/cloudinit/net/sysconfig.py -@@ -295,10 +295,10 @@ class Renderer(renderer.Renderer): - cls._render_subnet(iface_cfg, route_cfg, iface_subnets[0]) - elif len(iface_subnets) > 1: - for i, iface_subnet in enumerate(iface_subnets, -- start=len(iface.children)): -+ start=len(iface_cfg.children)): - iface_sub_cfg = iface_cfg.copy() - iface_sub_cfg.name = "%s:%s" % (iface_name, i) -- iface.children.append(iface_sub_cfg) -+ iface_cfg.children.append(iface_sub_cfg) - cls._render_subnet(iface_sub_cfg, route_cfg, iface_subnet) - - @classmethod diff --git a/cloud-init-no-dmidecode-on-ppc64.patch b/cloud-init-no-dmidecode-on-ppc64.patch deleted file mode 100644 index f1d8893..0000000 --- a/cloud-init-no-dmidecode-on-ppc64.patch +++ /dev/null @@ -1,22 +0,0 @@ ---- cloudinit/util.py.orig -+++ cloudinit/util.py -@@ -2337,7 +2337,7 @@ def read_dmi_data(key): - - # running dmidecode can be problematic on some arches (LP: #1243287) - uname_arch = os.uname()[4] -- if uname_arch.startswith("arm") or uname_arch == "aarch64": -+ if uname_arch.startswith("arm") or uname_arch.startswith("ppc"): - LOG.debug("dmidata is not supported on %s", uname_arch) - return None - ---- tests/unittests/test_util.py.orig -+++ tests/unittests/test_util.py -@@ -384,7 +384,7 @@ class TestReadDMIData(helpers.Filesystem - dmi_name = 'use-dmidecode' - self._configure_dmidecode_return(dmi_name, dmi_val) - -- expected = {'armel': None, 'aarch64': None, 'x86_64': dmi_val} -+ expected = {'armel': None, 'aarch64': dmi_val, 'x86_64': dmi_val} - found = {} - # we do not run the 'dmi-decode' binary on some arches - # verify that anything requested that is not in the sysfs dir diff --git a/cloud-init-python2-sigpipe.patch b/cloud-init-python2-sigpipe.patch index a2094e8..3ae8b1f 100644 --- a/cloud-init-python2-sigpipe.patch +++ b/cloud-init-python2-sigpipe.patch @@ -1,20 +1,20 @@ --- cloudinit/util.py.orig +++ cloudinit/util.py -@@ -46,6 +46,7 @@ import tempfile - import time +@@ -35,6 +35,7 @@ import time + from errno import ENOENT, ENOEXEC from base64 import b64decode, b64encode +from signal import signal, SIGPIPE, SIG_DFL from six.moves.urllib import parse as urlparse import six -@@ -1802,7 +1803,8 @@ def subp(args, data=None, rcs=None, env= +@@ -1815,7 +1816,8 @@ def subp(args, data=None, rcs=None, env= sp = subprocess.Popen(args, stdout=stdout, stderr=stderr, stdin=stdin, - env=env, shell=shell) + env=env, shell=shell, -+ preexec_fn=lambda: signal(SIGPIPE, SIG_DFL)) ++ preexec_fn=lambda: signal(SIGPIPE, SIG_DFL)) (out, err) = sp.communicate(data) # Just ensure blank instead of none. diff --git a/cloud-init-python26.patch b/cloud-init-python26.patch deleted file mode 100644 index d540409..0000000 --- a/cloud-init-python26.patch +++ /dev/null @@ -1,23 +0,0 @@ -Index: cloud-init-0.7.8/cloudinit/util.py -=================================================================== ---- cloud-init-0.7.8.orig/cloudinit/util.py -+++ cloud-init-0.7.8/cloudinit/util.py -@@ -283,9 +283,6 @@ class ProcessExecutionError(IOError): - 'reason': self.reason, - } - IOError.__init__(self, message) -- # For backward compatibility with Python 2. -- if not hasattr(self, 'message'): -- self.message = message - - - class SeLinuxGuard(object): -@@ -1816,7 +1813,7 @@ def subp(args, data=None, rcs=None, env= - def ldecode(data, m='utf-8'): - if not isinstance(data, bytes): - return data -- return data.decode(m, errors=decode) -+ return data.decode(m, decode) - - out = ldecode(out) - err = ldecode(err) diff --git a/cloud-init-service.patch b/cloud-init-service.patch deleted file mode 100644 index 3829df4..0000000 --- a/cloud-init-service.patch +++ /dev/null @@ -1,58 +0,0 @@ -Index: systemd/cloud-init.service -=================================================================== ---- systemd/cloud-init.service.orig -+++ systemd/cloud-init.service -@@ -1,9 +1,19 @@ - [Unit] - Description=Initial cloud-init job (metadata service crawler) --After=cloud-init-local.service networking.service --Before=network-online.target sshd.service sshd-keygen.service systemd-user-sessions.service --Requires=networking.service --Wants=local-fs.target cloud-init-local.service sshd.service sshd-keygen.service -+DefaultDependencies=no -+Wants=cloud-init-local.service -+Wants=local-fs.target -+Wants=sshd-keygen.service -+Wants=sshd.service -+After=cloud-init-local.service -+After=dbus.service -+After=wicked.service -+Requires=wicked.service -+Before=network-online.target -+Before=sshd-keygen.service -+Before=sshd.service -+Before=systemd-user-sessions.service -+Conflicts=shutdown.target - - [Service] - Type=oneshot -Index: systemd/cloud-init-local.service -=================================================================== ---- systemd/cloud-init-local.service.orig -+++ systemd/cloud-init-local.service -@@ -4,9 +4,10 @@ DefaultDependencies=no - Wants=local-fs.target - Wants=network-pre.target - After=local-fs.target --Conflicts=shutdown.target -+Before=basic.target - Before=network-pre.target - Before=shutdown.target -+Conflicts=shutdown.target - - [Service] - Type=oneshot -Index: systemd/cloud-final.service -=================================================================== ---- systemd/cloud-final.service.orig -+++ systemd/cloud-final.service -@@ -1,6 +1,8 @@ - [Unit] - Description=Execute cloud user/final scripts --After=network-online.target cloud-config.service rc-local.service multi-user.target -+After=cloud-config.service -+After=network-online.target -+After=rc-local.service - Before=systemd-logind.service - Wants=network-online.target cloud-config.service - diff --git a/cloud-init-set-variant.patch b/cloud-init-set-variant.patch new file mode 100644 index 0000000..68ad8f6 --- /dev/null +++ b/cloud-init-set-variant.patch @@ -0,0 +1,11 @@ +--- cloudinit/util.py.orig ++++ cloudinit/util.py +@@ -599,6 +599,8 @@ def system_info(): + var = 'ubuntu' + elif linux_dist == 'redhat': + var = 'rhel' ++ elif linux_dist == 'suse': ++ var = 'suse' + else: + var = 'linux' + elif system in ('windows', 'darwin', "freebsd"): diff --git a/cloud-init-spceandtabs-clean.patch b/cloud-init-spceandtabs-clean.patch deleted file mode 100644 index 9d894eb..0000000 --- a/cloud-init-spceandtabs-clean.patch +++ /dev/null @@ -1,74 +0,0 @@ -Index: cloud-init-0.7.8/cloudinit/net/__init__.py -=================================================================== ---- cloud-init-0.7.8.orig/cloudinit/net/__init__.py -+++ cloud-init-0.7.8/cloudinit/net/__init__.py -@@ -38,7 +38,7 @@ def read_sys_net(devname, path, translat - contents = util.load_file(dev_path) - except (OSError, IOError) as e: - e_errno = getattr(e, 'errno', None) -- if e_errno in (errno.ENOENT, errno.ENOTDIR): -+ if e_errno in (errno.ENOENT, errno.ENOTDIR): - if enoent is not None: - return enoent - raise -@@ -173,16 +173,16 @@ def generate_fallback_config(): - if name not in potential_interfaces: - continue - mac = read_sys_net(name, 'address', enoent=False) -- if mac: -+ if mac: - target_name = name -- target_mac = mac -- break -+ target_mac = mac -+ break - if target_mac and target_name: - nconf = {'config': [], 'version': 1} -- nconf['config'].append( -+ nconf['config'].append( - {'type': 'physical', 'name': target_name, -- 'mac_address': target_mac, 'subnets': [{'type': 'dhcp'}]}) -- return nconf -+ 'mac_address': target_mac, 'subnets': [{'type': 'dhcp'}]}) -+ return nconf - else: - return None - -Index: cloud-init-0.7.8/cloudinit/net/eni.py -=================================================================== ---- cloud-init-0.7.8.orig/cloudinit/net/eni.py -+++ cloud-init-0.7.8/cloudinit/net/eni.py -@@ -338,7 +338,7 @@ class Renderer(renderer.Renderer): - up = indent + "post-up route add" - down = indent + "pre-down route del" - or_true = " || true" -- gateway = indent + "gateway " -+ gateway = indent + "gateway " - mapping = { - 'network': '-net', - 'netmask': 'netmask', -@@ -347,7 +347,7 @@ class Renderer(renderer.Renderer): - } - if route['network'] == '0.0.0.0' and route['netmask'] == '0.0.0.0': - default_gw = " default gw %s" % route['gateway'] -- content.append(gateway + route['gateway']) -+ content.append(gateway + route['gateway']) - content.append(up + default_gw + or_true) - content.append(down + default_gw + or_true) - elif route['network'] == '::' and route['netmask'] == 0: -Index: cloud-init-0.7.8/cloudinit/net/cmdline.py -=================================================================== ---- cloud-init-0.7.8.orig/cloudinit/net/cmdline.py -+++ cloud-init-0.7.8/cloudinit/net/cmdline.py -@@ -198,9 +198,9 @@ def read_kernel_cmdline_config(files=Non - - if mac_addrs is None: - mac_addrs = {} -- for k in get_devicelist(): -+ for k in get_devicelist(): - mac_addr = read_sys_net(k, 'address', enoent=False) -- if mac_addr: -+ if mac_addr: - mac_addrs[k] = mac_addr - - return config_from_klibc_net_cfg(files=files, mac_addrs=mac_addrs) diff --git a/cloud-init-sysconfig-netpathfix.patch b/cloud-init-sysconfig-netpathfix.patch index 8c9c716..a1da7dd 100644 --- a/cloud-init-sysconfig-netpathfix.patch +++ b/cloud-init-sysconfig-netpathfix.patch @@ -1,15 +1,17 @@ --- cloudinit/net/sysconfig.py.orig +++ cloudinit/net/sysconfig.py -@@ -94,7 +94,7 @@ class ConfigMap(object): +@@ -88,8 +88,8 @@ class ConfigMap(object): class Route(ConfigMap): """Represents a route configuration.""" -- route_fn_tpl = '%(base)s/network-scripts/route-%(name)s' -+ route_fn_tpl = '%(base)s/network/route-%(name)s' +- route_fn_tpl_ipv4 = '%(base)s/network-scripts/route-%(name)s' +- route_fn_tpl_ipv6 = '%(base)s/network-scripts/route6-%(name)s' ++ route_fn_tpl_ipv4 = '%(base)s/network/route-%(name)s' ++ route_fn_tpl_ipv6 = '%(base)s/network/route6-%(name)s' def __init__(self, route_name, base_sysconf_dir): super(Route, self).__init__() -@@ -119,7 +119,7 @@ class Route(ConfigMap): +@@ -166,7 +166,7 @@ class Route(ConfigMap): class NetInterface(ConfigMap): """Represents a sysconfig/networking-script (and its config + children).""" @@ -18,3 +20,14 @@ iface_types = { 'ethernet': 'Ethernet', +@@ -602,8 +602,8 @@ def available(target=None): + return False + + expected_paths = [ +- 'etc/sysconfig/network-scripts/network-functions', +- 'etc/sysconfig/network-scripts/ifdown-eth'] ++ 'etc/sysconfig/network/network-functions', ++ 'etc/sysconfig/network/ifdown-eth'] + for p in expected_paths: + if not os.path.isfile(util.target_path(target, p)): + return False diff --git a/cloud-init-tests-set-exec.patch b/cloud-init-tests-set-exec.patch new file mode 100644 index 0000000..77ed626 --- /dev/null +++ b/cloud-init-tests-set-exec.patch @@ -0,0 +1,20 @@ +--- Makefile.orig ++++ Makefile +@@ -11,7 +11,7 @@ PIP_INSTALL := pip install + + ifeq ($(PYVER),python3) + pyflakes = pyflakes3 +- unittests = unittest3 ++ unittests = unittest + yaml = yaml + else + ifeq ($(PYVER),python2) +@@ -19,7 +19,7 @@ ifeq ($(PYVER),python2) + unittests = unittest + else + pyflakes = pyflakes pyflakes3 +- unittests = unittest unittest3 ++ unittests = unittest unittest + endif + endif + diff --git a/cloud-init.changes b/cloud-init.changes index eb7da5d..06ddbf6 100644 --- a/cloud-init.changes +++ b/cloud-init.changes @@ -1,3 +1,555 @@ +------------------------------------------------------------------- +Thu Sep 21 17:32:55 EDT 2017 - rjschwei@suse.com + +- Update to version 17.1 + + Version numbering scheme change now YY.NUMBER_OF_RELESE_THAT_YEAR + + Remove cloud.cfg.suse, use generated default config file + + Remove addopenSUSEBase.patch, included upstream + + Remove suseIntegratedHandler.patch, included upstream + + Remove openSUSEhostsTemplate.diff, included upstream + + Remove cloud-init-handle-no-carrier.patch, included upstream + + Remove cloud-init-digital-ocean-datasource.patch, + use upstream implementation + + Remove cloud-init-digital-ocean-datasource-enable-by-default.patch, + use upstream implementation + + Remove cloud-init-fix-unicode-handling-binarydecode.patch, + included upstream + + Remove cloud-init-no-dmidecode-on-ppc64.patch, included upstream + + Remove dataSourceOpenNebula.patch, use upstream implementation + + Remove setupSUSEsysVInit.diff, included upstream + + Remove SUSEsysVInit.diff, included upstream + + Remove cloud-init-finalbeforelogin.patch, don't block login + + Remove cloud-init-handle-not-implemented-query.patch, query option removed + + Remove cloud-init-spceandtabs-clean.patch, indentation fixed upstream + + Remove dynamicInitCmd.diff, different solution from upstream + + Added cloud-init-more-tasks.patch, (bsc#1047363) + replace cloud-init-finalbeforelogin.patch + + Forward port cloud-init-python2-sigpipe.patch + + Remove cloud-init-net-eni.patch, included upstream + + Remove cloud-init-service.patch, included upstream + + Forward port cloud-init-sysconfig-netpathfix.patch + + Remove cloud-init-net-sysconfig-lp1665441.patch, included upstream + + Remove cloud-init-python26.patch, included upstream + + Add cloud-init-tests-set-exec.patch + + Add cloud-init-final-no-apt.patch + + Remove skip-argparse-on-python3.patch + + doc: document GCE datasource. [Arnd Hannemann] + + suse: updates to templates to support openSUSE and SLES. + + [Robert Schweikert] (LP: #1718640) + + suse: Copy sysvinit files from redhat with slight changes. + + [Robert Schweikert] (LP: #1718649) + + docs: fix sphinx module schema documentation [Chad Smith] + + tests: Add cloudinit package to all test targets [Chad Smith] + + Makefile: No longer look for yaml files in obsolete ./bin/. + + tests: fix ds-identify unit tests to set EC2_STRICT_ID_DEFAULT. + + ec2: Fix maybe_perform_dhcp_discovery to use /var/tmp as a tmpdir + + [Chad Smith] (LP: #1717627) + + Azure: wait longer for SSH pub keys to arrive. + + [Paul Meyer] (LP: #1717611) + + GCE: Fix usage of user-data. (LP: #1717598) + + cmdline: add collect-logs subcommand. [Chad Smith] (LP: #1607345) + + CloudStack: consider dhclient lease files named with a hyphen. + + (LP: #1717147) + + resizefs: Drop check for read-only device file, do not warn on + + overlayroot. [Chad Smith] + + Do not provide systemd-fsck drop-in which could cause ordering cycles. + + [Balint Reczey] (LP: #1717477) + + tests: Enable the NoCloud KVM platform [Joshua Powers] + + resizefs: pass mount point to xfs_growfs [Dusty Mabe] + + vmware: Enable nics before sending the SUCCESS event. [Sankar Tanguturi] + + cloud-config modules: honor distros definitions in each module + + [Chad Smith] (LP: #1715738, #1715690) + + chef: Add option to pin chef omnibus install version + + [Ethan Apodaca] (LP: #1462693) + + tests: execute: support command as string [Joshua Powers] + + schema and docs: Add jsonschema to resizefs and bootcmd modules + + [Chad Smith] + + tools: Add xkvm script, wrapper around qemu-system [Joshua Powers] + + vmware customization: return network config format + + [Sankar Tanguturi] (LP: #1675063) + + Ec2: only attempt to operate at local mode on known platforms. + + (LP: #1715128) + + Use /run/cloud-init for tempfile operations. (LP: #1707222) + + ds-identify: Make OpenStack return maybe on arch other than intel. + + (LP: #1715241) + + tests: mock missed openstack metadata uri network_data.json + + [Chad Smith] (LP: #1714376) + + relocate tests/unittests/helpers.py to cloudinit/tests + + [Lars Kellogg-Stedman] + + tox: add nose timer output [Joshua Powers] + + upstart: do not package upstart jobs, drop ubuntu-init-switch module. + + tests: Stop leaking calls through unmocked metadata addresses + + [Chad Smith] (LP: #1714117) + + distro: allow distro to specify a default locale [Ryan Harper] + + tests: fix two recently added tests for sles distro. + + url_helper: dynamically import oauthlib import from inside oauth_headers + + [Chad Smith] + + tox: make xenial environment run with python3.6 + + suse: Add support for openSUSE and return SLES to a working state. + + [Robert Schweikert] + + GCE: Add a main to the GCE Datasource. + + ec2: Add IPv6 dhcp support to Ec2DataSource. [Chad Smith] (LP: #1639030) + + url_helper: fail gracefully if oauthlib is not available + + [Lars Kellogg-Stedman] (LP: #1713760) + + cloud-init analyze: fix issues running under python 2. [Andrew Jorgensen] + + Configure logging module to always use UTC time. + + [Ryan Harper] (LP: #1713158) + + Log a helpful message if a user script does not include shebang. + + [Andrew Jorgensen] + + cli: Fix command line parsing of coniditionally loaded subcommands. + + [Chad Smith] (LP: #1712676) + + doc: Explain error behavior in user data include file format. + + [Jason Butz] + + cc_landscape & cc_puppet: Fix six.StringIO use in writing configs + + [Chad Smith] (LP: #1699282, #1710932) + + schema cli: Add schema subcommand to cloud-init cli and cc_runcmd schema + + [Chad Smith] + + Debian: Remove non-free repositories from apt sources template. + + [Joonas Kylmälä] (LP: #1700091) + + tools: Add tooling for basic cloud-init performance analysis. + + [Chad Smith] (LP: #1709761) + + network: add v2 passthrough and fix parsing v2 config with bonds/bridge + + params [Ryan Harper] (LP: #1709180) + + doc: update capabilities with features available, link doc reference, + + cli example [Ryan Harper] + + vcloud directory: Guest Customization support for passwords + + [Maitreyee Saikia] + + ec2: Allow Ec2 to run in init-local using dhclient in a sandbox. + + [Chad Smith] (LP: #1709772) + + cc_ntp: fallback on timesyncd configuration if ntp is not installable + + [Ryan Harper] (LP: #1686485) + + net: Reduce duplicate code. Have get_interfaces_by_mac use + + get_interfaces. + + tests: Fix build tree integration tests [Joshua Powers] + + sysconfig: Dont repeat header when rendering resolv.conf + + [Ryan Harper] (LP: #1701420) + + archlinux: Fix bug with empty dns, do not render 'lo' devices. + + (LP: #1663045, #1706593) + + cloudinit.net: add initialize_network_device function and tests + + [Chad Smith] + + makefile: fix ci-deps-ubuntu target [Chad Smith] + + tests: adjust locale integration test to parse default locale. + + tests: remove 'yakkety' from releases as it is EOL. + + tests: Add initial tests for EC2 and improve a docstring. + + locale: Do not re-run locale-gen if provided locale is system default. + + archlinux: fix set hostname usage of write_file. + + [Joshua Powers] (LP: #1705306) + + sysconfig: support subnet type of 'manual'. + + tools/run-centos: make running with no argument show help. + + Drop rand_str() usage in DNS redirection detection + + [Bob Aman] (LP: #1088611) + + sysconfig: use MACADDR on bonds/bridges to configure mac_address + + [Ryan Harper] (LP: #1701417) + + net: eni route rendering missed ipv6 default route config + + [Ryan Harper] (LP: #1701097) + + sysconfig: enable mtu set per subnet, including ipv6 mtu + + [Ryan Harper] (LP: #1702513) + + sysconfig: handle manual type subnets [Ryan Harper] (LP: #1687725) + + sysconfig: fix ipv6 gateway routes [Ryan Harper] (LP: #1694801) + + sysconfig: fix rendering of bond, bridge and vlan types. + + [Ryan Harper] (LP: #1695092) + + Templatize systemd unit files for cross distro deltas. [Ryan Harper] + + sysconfig: ipv6 and default gateway fixes. [Ryan Harper] (LP: #1704872) + + net: fix renaming of nics to support mac addresses written in upper + + case. (LP: #1705147) + + tests: fixes for issues uncovered when moving to python 3.6. + + (LP: #1703697) + + sysconfig: include GATEWAY value if set in subnet + + [Ryan Harper] (LP: #1686856) + + Scaleway: add datasource with user and vendor data for Scaleway. + + [Julien Castets] + + Support comments in content read by load_shell_content. + + cloudinitlocal fail to run during boot [Hongjiang Zhang] + + doc: fix disk setup example table_type options + + [Sandor Zeestraten] (LP: #1703789) + + tools: Fix exception handling. [Joonas Kylmälä] (LP: #1701527) + + tests: fix usage of mock in GCE test. + + test_gce: Fix invalid mock of platform_reports_gce to return False + + [Chad Smith] + + test: fix incorrect keyid for apt repository. + + [Joshua Powers] (LP: #1702717) + + tests: Update version of pylxd [Joshua Powers] + + write_files: Remove log from helper function signatures. + + [Andrew Jorgensen] + + doc: document the cmdline options to NoCloud [Brian Candler] + + read_dmi_data: always return None when inside a container. (LP: #1701325) + + requirements.txt: remove trailing white space. + + Azure: Add network-config, Refactor net layer to handle duplicate macs. + + [Ryan Harper] + + Tests: Simplify the check on ssh-import-id [Joshua Powers] + + tests: update ntp tests after sntp added [Joshua Powers] + + FreeBSD: Make freebsd a variant, fix unittests and + + tools/build-on-freebsd. + + FreeBSD: fix test failure + + FreeBSD: replace ifdown/ifup with "ifconfig down" and "ifconfig up". + + [Hongjiang Zhang] (LP: #1697815) + + FreeBSD: fix cdrom mounting failure if /mnt/cdrom/secure did not exist. + + [Hongjiang Zhang] (LP: #1696295) + + main: Don't use templater to format the welcome message + + [Andrew Jorgensen] + + docs: Automatically generate module docs form schema if present. + + [Chad Smith] + + debian: fix path comment in /etc/hosts template. + + [Jens Sandmann] (LP: #1606406) + + suse: add hostname and fully qualified domain to template. + + [Jens Sandmann] + + write_file(s): Print permissions as octal, not decimal [Andrew Jorgensen] + + ci deps: Add --test-distro to read-dependencies to install all deps + + [Chad Smith] + + tools/run-centos: cleanups and move to using read-dependencies + + pkg build ci: Add make ci-deps- target to install pkgs + + [Chad Smith] + + systemd: make cloud-final.service run before apt daily services. + + (LP: #1693361) + + selinux: Allow restorecon to be non-fatal. [Ryan Harper] (LP: #1686751) + + net: Allow netinfo subprocesses to return 0 or 1. + + [Ryan Harper] (LP: #1686751) + + net: Allow for NetworkManager configuration [Ryan McCabe] (LP: #1693251) + + Use distro release version to determine if we use systemd in redhat spec + + [Ryan Harper] + + net: normalize data in network_state object + + Integration Testing: tox env, pyxld 2.2.3, and revamp framework + + [Wesley Wiedenmeier] + + Chef: Update omnibus url to chef.io, minor doc changes. [JJ Asghar] + + tools: add centos scripts to build and test [Joshua Powers] + + Drop cheetah python module as it is not needed by trunk [Ryan Harper] + + rhel/centos spec cleanups. + + cloud.cfg: move to a template. setup.py changes along the way. + + Makefile: add deb-src and srpm targets. use PYVER more places. + + makefile: fix python 2/3 detection in the Makefile [Chad Smith] + + snap: Removing snapcraft plug line [Joshua Powers] (LP: #1695333) + + RHEL/CentOS: Fix default routes for IPv4/IPv6 configuration. + + [Andreas Karis] (LP: #1696176) + + test: Fix pyflakes complaint of unused import. + + [Joshua Powers] (LP: #1695918) + + NoCloud: support seed of nocloud from smbios information + + [Vladimir Pouzanov] (LP: #1691772) + + net: when selecting a network device, use natural sort order + + [Marc-Aurèle Brothier] + + fix typos and remove whitespace in various docs [Stephan Telling] + + systemd: Fix typo in comment in cloud-init.target. [Chen-Han Hsiao] + + Tests: Skip jsonschema related unit tests when dependency is absent. + + [Chad Smith] (LP: #1695318) + + azure: remove accidental duplicate line in merge. + + azure: identify platform by well known value in chassis asset tag. + + [Chad Smith] (LP: #1693939) + + tools/net-convert.py: support old cloudinit versions by using kwargs. + + ntp: Add schema definition and passive schema validation. + + [Chad Smith] (LP: #1692916) + + Fix eni rendering for bridge params that require repeated key for + + values. [Ryan Harper] + + net: remove systemd link file writing from eni renderer [Ryan Harper] + + AliYun: Enable platform identification and enable by default. + + [Junjie Wang] (LP: #1638931) + + net: fix reading and rendering addresses in cidr format. + + [Dimitri John Ledkov] (LP: #1689346, #1684349) + + disk_setup: udev settle before attempting partitioning or fs creation. + + (LP: #1692093) + + GCE: Update the attribute used to find instance SSH keys. + + [Daniel Watkins] (LP: #1693582) + + nplan: For bonds, allow dashed or underscore names of keys. + + [Dimitri John Ledkov] (LP: #1690480) + + python2.6: fix unit tests usage of assertNone and format. + + test: update docstring on test_configured_list_with_none + + fix tools/ds-identify to not write None twice. + + tox/build: do not package depend on style requirements. + + cc_ntp: Restructure cc_ntp unit tests. [Chad Smith] (LP: #1692794) + + flake8: move the pinned version of flake8 up to 3.3.0 + + tests: Apply workaround for snapd bug in test case. [Joshua Powers] + + RHEL/CentOS: Fix dual stack IPv4/IPv6 configuration. + + [Andreas Karis] (LP: #1679817, #1685534, #1685532) + + disk_setup: fix several issues with gpt disk partitions. (LP: #1692087) + + function spelling & docstring update [Joshua Powers] + + Fixing wrong file name regression. [Joshua Powers] + + tox: move pylint target to 1.7.1 + + Fix get_interfaces_by_mac for empty macs (LP: #1692028) + + DigitalOcean: remove routes except for the public interface. + + [Ben Howard] (LP: #1681531.) + + netplan: pass macaddress, when specified, for vlans + + [Dimitri John Ledkov] (LP: #1690388) + + doc: various improvements for the docs on cc_users_groups. + + [Felix Dreissig] + + cc_ntp: write template before installing and add service restart + + [Ryan Harper] (LP: #1645644) + + cloudstack: fix tests to avoid accessing /var/lib/NetworkManager + + [Lars Kellogg-Stedman] + + tests: fix hardcoded path to mkfs.ext4 [Joshua Powers] (LP: #1691517) + + Actually skip warnings when .skip file is present. + + [Chris Brinker] (LP: #1691551) + + netplan: fix netplan render_network_state signature. + + [Dimitri John Ledkov] (LP: #1685944) + + Azure: fix reformatting of ephemeral disks on resize to large types. + + (LP: #1686514) + + Revert "tools/net-convert: fix argument order for render_network_state" + + make deb: Add devscripts dependency for make deb. Cleanup + + packages/bddeb. [Chad Smith] (LP: #1685935) + + tools/net-convert: fix argument order for render_network_state + + [Ryan Harper] (LP: #1685944) + + openstack: fix log message copy/paste typo in _get_url_settings + + [Lars Kellogg-Stedman] + + unittests: fix unittests run on centos [Joshua Powers] + + Improve detection of snappy to include os-release and kernel cmdline. + + (LP: #1689944) + + Add address to config entry generated by _klibc_to_config_entry. + + [Julien Castets] (LP: #1691135) + + sysconfig: Raise ValueError when multiple default gateways are present. + + [Chad Smith] (LP: #1687485) + + FreeBSD: improvements and fixes for use on Azure + + [Hongjiang Zhang] (LP: #1636345) + + Add unit tests for ds-identify, fix Ec2 bug found. + + fs_setup: if cmd is specified, use shell interpretation. + + [Paul Meyer] (LP: #1687712) + + doc: document network configuration defaults policy and formats. + + [Ryan Harper] + + Fix name of "uri" key in docs for "cc_apt_configure" module + + [Felix Dreissig] + + tests: Enable artful [Joshua Powers] + + nova-lxd: read product_name from environment, not platform. + + (LP: #1685810) + + Fix yum repo config where keys contain array values + + [Dylan Perry] (LP: #1592150) + + template: Update debian backports template [Joshua Powers] (LP: #1627293) + + rsyslog: replace ~ with stop [Joshua Powers] (LP: #1367899) + + Doc: add additional RTD examples [Joshua Powers] (LP: #1459604) + + Fix growpart for some cases when booted with root=PARTUUID. + + (LP: #1684869) + + pylint: update output style to parseable [Joshua Powers] + + pylint: fix all logging warnings [Joshua Powers] + + CloudStack: Add NetworkManager to list of supported DHCP lease dirs. + + [Syed] + + net: kernel lies about vlans not stealing mac addresses, when they do + + [Dimitri John Ledkov] (LP: #1682871) + + ds-identify: Check correct path for "latest" config drive + + [Daniel Watkins] (LP: #1673637) + + doc: Fix example for resolve.conf configuration. + + [Jon Grimm] (LP: #1531582) + + Fix examples that reference upstream chef repository. + + [Jon Grimm] (LP: #1678145) + + doc: correct grammar and improve clarity in merging documentation. + + [David Tagatac] + + doc: Add missing doc link to snap-config module. [Ryan Harper] + + snap: allows for creating cloud-init snap [Joshua Powers] + + DigitalOcean: assign IPv4ll address to lowest indexed interface. + + [Ben Howard] + + DigitalOcean: configure all NICs presented in meta-data. [Ben Howard] + + Remove (and/or fix) URL shortener references [Jon Grimm] (LP: #1669727) + + HACKING.rst: more info on filling out contributors agreement. + + util: teach write_file about copy_mode option + + [Lars Kellogg-Stedman] (LP: #1644064) + + DigitalOcean: bind resolvers to loopback interface. [Ben Howard] + + tests: fix AltCloud tests to not rely on blkid (LP: #1636531) + + OpenStack: add 'dvs' to the list of physical link types. (LP: #1674946) + + Fix bug that resulted in an attempt to rename bonds or vlans. + + (LP: #1669860) + + tests: update OpenNebula and Digital Ocean to not rely on host + + interfaces. + + net: in netplan renderer delete known image-builtin content. + + (LP: #1675576) + + doc: correct grammar in capabilities.rst [David Tagatac] + + ds-identify: fix detecting of maas datasource. (LP: #1677710) + + netplan: remove debugging prints, add debug logging [Ryan Harper] + + ds-identify: do not write None twice to datasource_list. + + support resizing partition and rootfs on system booted without + + initramfs. [Steve Langasek] (LP: #1677376) + + apt_configure: run only when needed. (LP: #1675185) + + OpenStack: identify OpenStack by product 'OpenStack Compute'. + + (LP: #1675349) + + GCE: Search GCE in ds-identify, consider serial number in check. + + (LP: #1674861) + + Add support for setting hashed passwords [Tore S. Lonoy] (LP: #1570325) + + Fix filesystem creation when using "partition: auto" + + [Jonathan Ballet] (LP: #1634678) + + ConfigDrive: support reading config drive data from /config-drive. + + (LP: #1673411) + + ds-identify: fix detection of Bigstep datasource. (LP: #1674766) + + test: add running of pylint [Joshua Powers] + + ds-identify: fix bug where filename expansion was left on. + + advertise network config v2 support (NETWORK_CONFIG_V2) in features. + + Bigstep: fix bug when executing in python3. [root] + + Fix unit test when running in a system deployed with cloud-init. + + Bounce network interface for Azure when using the built-in path. + + [Brent Baude] (LP: #1674685) + + cloudinit.net: add network config v2 parsing and rendering [Ryan Harper] + + net: Fix incorrect call to isfile [Joshua Powers] (LP: #1674317) + + net: add renderers for automatically selecting the renderer. + + doc: fix config drive doc with regard to unpartitioned disks. + + (LP: #1673818) + + test: Adding integratiron test for password as list [Joshua Powers] + + render_network_state: switch arguments around, do not require target + + support 'loopback' as a device type. + + Integration Testing: improve testcase subclassing [Wesley Wiedenmeier] + + gitignore: adding doc/rtd_html [Joshua Powers] + + doc: add instructions for running integration tests via tox. + + [Joshua Powers] + + test: avoid differences in 'date' output due to daylight savings. + + Fix chef config module in omnibus install. [Jeremy Melvin] (LP: #1583837) + + Add feature flags to cloudinit.version. [Wesley Wiedenmeier] + + tox: add a citest environment + + Further fix regression to support 'password' for default user. + + fix regression when no chpasswd/list was provided. + + Support chpasswd/list being a list in addition to a string. + + [Sergio Lystopad] (LP: #1665694) + + doc: Fix configuration example for cc_set_passwords module. + + [Sergio Lystopad] (LP: #1665773) + + net: support both ipv4 and ipv6 gateways in sysconfig. + + [Lars Kellogg-Stedman] (LP: #1669504) + + net: do not raise exception for > 3 nameservers + + [Lars Kellogg-Stedman] (LP: #1670052) + + ds-identify: report cleanups for config and exit value. (LP: #1669949) + + ds-identify: move default setting for Ec2/strict_id to a global. + + ds-identify: record not found in cloud.cfg and always add None. + + Support warning if the used datasource is not in ds-identify's list. + + tools/ds-identify: make report mode write namespaced results. + + Move warning functionality to cloudinit/warnings.py + + Add profile.d script for showing warnings on login. + + Z99-cloud-locale-test.sh: install and make consistent. + + tools/ds-identify: look at cloud.cfg when looking for ec2 strict_id. + + tools/ds-identify: disable vmware_guest_customization by default. + + tools/ds-identify: ovf identify vmware guest customization. + + Identify Brightbox as an Ec2 datasource user. (LP: #1661693) + + DatasourceEc2: add warning message when not on AWS. + + ds-identify: add reading of datasource/Ec2/strict_id + + tools/ds-identify: add support for found or maybe contributing config. + + tools/ds-identify: read the seed directory on Ec2 + + tools/ds-identify: use quotes in local declarations. + + tools/ds-identify: fix documentation of policy setting in a comment. + + ds-identify: only run once per boot unless --force is given. + + flake8: fix flake8 complaints in previous commit. + + net: correct errors in cloudinit/net/sysconfig.py + + [Lars Kellogg-Stedman] (LP: #1665441) + + ec2_utils: fix MetadataLeafDecoder that returned bytes on empty + + apply the runtime configuration written by ds-identify. + + ds-identify: fix checking for filesystem label (LP: #1663735) + + ds-identify: read ds=nocloud properly (LP: #1663723) + + support nova-lxd by reading platform from environment of pid 1. + + (LP: #1661797) + + ds-identify: change aarch64 to use the default for non-dmi systems. + + Remove style checking during build and add latest style checks to tox + + [Joshua Powers] (LP: #1652329) + + code-style: make master pass pycodestyle (2.3.1) cleanly, currently: + + [Joshua Powers] + + manual_cache_clean: When manually cleaning touch a file in instance dir. + + Add tools/ds-identify to identify datasources available. + + Fix small typo and change iso-filename for consistency [Robin Naundorf] + + Fix eni rendering of multiple IPs per interface + + [Ryan Harper] (LP: #1657940) + + tools/mock-meta: support python2 or python3 and ipv6 in both. + + tests: remove executable bit on test_net, so it runs, and fix it. + + tests: No longer monkey patch httpretty for python 3.4.2 + + Add 3 ecdsa-sha2-nistp* ssh key types now that they are standardized + + [Lars Kellogg-Stedman] (LP: #1658174) + + reset httppretty for each test [Lars Kellogg-Stedman] (LP: #1658200) + + build: fix running Make on a branch with tags other than master + + EC2: Do not cache security credentials on disk + + [Andrew Jorgensen] (LP: #1638312) + + doc: Fix typos and clarify some aspects of the part-handler + + [Erik M. Bray] + + doc: add some documentation on OpenStack datasource. + + OpenStack: Use timeout and retries from config in get_data. + + [Lars Kellogg-Stedman] (LP: #1657130) + + Fixed Misc issues related to VMware customization. [Sankar Tanguturi] + + Fix minor docs typo: perserve > preserve [Jeremy Bicha] + + Use dnf instead of yum when available + + [Lars Kellogg-Stedman] (LP: #1647118) + + validate-yaml: use python rather than explicitly python3 + + Get early logging logged, including failures of cmdline url. +- From 0.7.9 + + doc: adjust headers in tests documentation for consistency. + + pep8: fix issue found in zesty build with pycodestyle. + + integration test: initial commit of integration test framework + + [Wesley Wiedenmeier] + + LICENSE: Allow dual licensing GPL-3 or Apache 2.0 [Jon Grimm] + + Fix config order of precedence, putting kernel command line over system. + + [Wesley Wiedenmeier] (LP: #1582323) + + pep8: whitespace fix + + Update the list of valid ssh keys. [Michael Felt] + + network: add ENI unit test for statically rendered routes. + + set_hostname: avoid erroneously appending domain to fqdn + + [Lars Kellogg-Stedman] (LP: #1647910) + + doc: change 'nobootwait' to 'nofail' in docs [Anhad Jai Singh] + + Replace an expired bit.ly link in code comment. + + user-groups: fix bug when groups was provided as string and had spaces + + (LP: #1354694) + + mounts: use mount -a again to accomplish mounts (LP: #1647708) + + CloudSigma: Fix bug where datasource was not loaded in local search. + + (LP: #1648380) + + when adding a user, strip whitespace from group list [Lars Kellogg-Stedman] + + (LP: #1354694) + + fix decoding of utf-8 chars in yaml test + + Replace usage of sys_netdev_info with read_sys_net (LP: #1625766) + + fix problems found in python2.6 test. + + OpenStack: extend physical types to include hyperv, hw_veb, vhost_user. + + (LP: #1642679) + + tests: fix assumptions that expected no eth0 in system. (LP: #1644043) + + net/cmdline: Consider ip= or ip6= on command line not only ip= + + (LP: #1639930) + + Just use file logging by default (LP: #1643990) + + Improve formatting for ProcessExecutionError [Wesley Wiedenmeier] + + flake8: fix trailing white space + + Doc: various documentation fixes [Sean Bright] + + cloudinit/config/cc_rh_subscription.py: Remove repos before adding + + [Brent Baude] + + packages/redhat: fix rpm spec file. + + main: set TZ in environment if not already set. [Ryan Harper] + + Azure: No longer rely on walinux agent. (LP: #1538522) + + disk_setup: Use sectors as unit when formatting MBR disks with sfdisk. + + [Daniel Watkins] (LP: #1460715) + + Add activate_datasource, for datasource specific code paths. (LP: #1611074) + + systemd: cloud-init-local use RequiresMountsFor=/var/lib/cloud + + (LP: #1642062) + + systemd: cloud-init remove After=systemd-networkd-wait-online + + systemd: cloud-init-local change Before basic to sysinit + + pep8: fix style errors reported by pycodestyle 2.1.0 + + systemd: drop both Wants and After local-fs.target + + systemd: networking service adjustments. (LP: #1636912) + + systemd: replace Before=basic.target, dbus.target with sysinit.target + + (LP: #1629797) + + doc: Add documentation on stages of boot. + + doc: make the RST files consistently formated and other improvements. + + Ec2: fix syntax and tox in previous commit. + + Ec2: protect against non-dictionary in block-device-mapping. + + doc: fixed example to not overwrite /etc/hosts [Chris Glass] + + Doc: fix spelling / typos in ca_certs and scripts_vendor. + + pyflakes: fix issue with pyflakes 1.3 found in ubuntu zesty-proposed. + + net/cmdline: Further adjustments to ipv6 support [LaMont Jones] + + (LP: #1621615) + + Add coverage dependency to bddeb to fix package build. + + doc: improve HACKING.rst file + + dmidecode: Allow dmidecode to be used on aarch64 [Robert Schweikert] + + AliYun: Add new datasource for Ali-Cloud ECS [kaihuan.pkh] + + Add coverage collection to tox unit tests. [Joshua Powers] + + cc_users_groups: fix remaing call to ds.normalize_user_groups [Ryan Harper] + + disk-config: udev settle after partitioning in gpt format. (LP: #1626243) + + unittests: do not read system /etc/cloud/cloud.cfg.d (LP: #1635350) + + Add documentation for logging features. [Wesley Wiedenmeier] + + Add support for snap create-user on Ubuntu Core images. [Ryan Harper] + + Fix sshd restarts for rhel distros. [Jim Gorz] + + OpenNebula: replace 'ip' parsing with cloudinit.net usage. + + Fix python2.6 things found running in centos 6. + + Move user/group functions to new ug_util file + + DigitalOcean: enable usage of data source by default. + + update Gentoo initscripts to run in the correct order [Matthew Thode] + + MAAS: improve the main of datasource to look at kernel cmdline config. + + tests: silence the Cheetah UserWarning about NameMapper C version. + + systemd: Run cloud-init.service Before dbus.socket not dbus.target + + [Daniel Watkins] (LP: #1629797) + + systemd: run cloud-init.service Before dbus.service (LP: #1629797) + + unittests: fix use of mock 2.0 'assert_called' when running make check + + [Ryan Harper] + + Improve module documentation and doc cleanup. [Wesley Wiedenmeier] + + lxd: Update network config for LXD 2.3 [Stéphane Graber] + + DigitalOcean: use meta-data for network configruation [Ben Howard] + + ntp: move to run after apt configuration (LP: #1628337) + + Decode unicode types in decode_binary [Robert Schweikert] + + systemd: Ensure that cloud-init-local happens before NetworkManager + + Allow ephemeral drive to be unpartitioned [Paul Meyer] + + subp: add 'update_env' argument + + net: support reading ipv6 dhcp config from initramfs [LaMont Jones] + + (LP: #1621615, #1621507) + + Adjust mounts and disk configuration for systemd. (LP: #1611074) + + dmidecode: run dmidecode only on i?86 or x86_64 arch. [Robert Schweikert] + + systemd: put cloud-init.target After multi-user.target (LP: #1623868) + ------------------------------------------------------------------- Wed Sep 20 10:11:42 UTC 2017 - dmueller@suse.com diff --git a/cloud-init.spec b/cloud-init.spec index 08e062d..62fe931 100644 --- a/cloud-init.spec +++ b/cloud-init.spec @@ -18,73 +18,59 @@ %global configver 0.7 Name: cloud-init -Version: 0.7.8 +Version: 17.1 Release: 0 License: GPL-3.0 and AGPL-3.0 Summary: Cloud node initialization tool Url: http://launchpad.net/cloud-init/ Group: System/Management Source0: %{name}-%{version}.tar.gz -Source1: cloud.cfg.suse -Source2: rsyslog-cloud-init.cfg -Patch0: suseSysVInit.diff -# FIXME addopenSUSEBase.patch proposed for upstream merge -Patch1: addopenSUSEBase.patch -# FIXME suseIntegratedHandler.patch proposed for upstream merge -Patch2: suseIntegratedHandler.patch -Patch3: setupSUSEsysVInit.diff -# FIXME openSUSEhostsTemplate.diff proposed for upstream merge -Patch5: openSUSEhostsTemplate.diff -# FIXME dynamicInitCmd.patch proposed for upstream merge -Patch6: dynamicInitCmd.diff -Patch9: cloud-init-no-dmidecode-on-ppc64.patch +Source1: rsyslog-cloud-init.cfg +# FIXME zypp_add_repos.diff needs proposed for upstream merge +Patch4: zypp_add_repos.diff Patch10: cloud-init-no-user-lock-if-already-locked.patch -Patch11: dataSourceOpenNebula.patch Patch12: fix-default-systemd-unit-dir.patch -Patch14: cloud-init-finalbeforelogin.patch +# FIXME cloud-init-more-tasks.patch proposed for upstream merge +Patch13: cloud-init-more-tasks.patch # python2 disables SIGPIPE, causing broken pipe errors in shell scripts (bsc#903449) Patch20: cloud-init-python2-sigpipe.patch -Patch21: cloud-init-net-eni.patch -Patch22: cloud-init-service.patch -Patch23: cloud-init-fix-unicode-handling-binarydecode.patch -# From upstream patch -Patch24: cloud-init-handle-no-carrier.patch -Patch25: cloud-init-digital-ocean-datasource.patch -Patch26: cloud-init-digital-ocean-datasource-enable-by-default.patch Patch27: cloud-init-sysconfig-netpathfix.patch -Patch28: zypp_add_repos.diff Patch29: datasourceLocalDisk.patch -Patch30: cloud-init-handle-not-implemented-query.patch -Patch32: cloud-init-net-sysconfig-lp1665441.patch -Patch33: cloud-init-spceandtabs-clean.patch -Patch34: skip-argparse-on-python3.patch +Patch34: cloud-init-tests-set-exec.patch +Patch35: cloud-init-final-no-apt.patch BuildRequires: fdupes BuildRequires: filesystem -%if 0%{?suse_version} && 0%{?suse_version} > 1315 -BuildRequires: python3-devel -BuildRequires: python3-setuptools -%else -BuildRequires: python-devel -BuildRequires: python-setuptools -%endif # pkg-config is needed to find correct systemd unit dir BuildRequires: pkg-config # needed for /lib/udev BuildRequires: udev %if 0%{?suse_version} > 1320 +BuildRequires: python3-devel +BuildRequires: python3-setuptools # Test requirements -#BuildRequires: python3-Cheetah BuildRequires: python3-Jinja2 BuildRequires: python3-PrettyTable BuildRequires: python3-PyYAML -BuildRequires: python3-configobj -#BuildRequires: python3-contextlib2 +BuildRequires: python3-configobj >= 5.0.2 BuildRequires: python3-httpretty BuildRequires: python3-jsonpatch BuildRequires: python3-mock +BuildRequires: python3-nose BuildRequires: python3-oauthlib BuildRequires: python3-requests BuildRequires: python3-testtools +%else +BuildRequires: python-devel +BuildRequires: python-Jinja2 +BuildRequires: python-PyYAML +BuildRequires: python-requests +BuildRequires: python-setuptools +BuildRequires: python-six +%endif +%if 0%{?is_opensuse} || 0%{?suse_version} == 1310 || 0%{?suse_version} == 1320 +BuildRequires: openSUSE-release +%else +BuildRequires: sles-release %endif Requires: bash Requires: file @@ -97,9 +83,10 @@ Requires: net-tools-deprecated Requires: openssh %if 0%{?suse_version} > 1320 Requires: python3-boto >= 2.7 -Requires: python3-configobj +Requires: python3-configobj >= 5.0.2 Requires: python3-Jinja2 Requires: python3-jsonpatch +Requires: python3-jsonschema Requires: python3-oauthlib Requires: python3-PrettyTable Requires: python3-pyserial @@ -111,9 +98,10 @@ Requires: python3-xml %else Requires: python-argparse Requires: python-boto >= 2.7 -Requires: python-configobj +Requires: python-configobj >= 5.0.2 Requires: python-Jinja2 Requires: python-jsonpatch +Requires: python-jsonschema Requires: python-oauthlib Requires: python-PrettyTable Requires: python-pyserial @@ -140,7 +128,6 @@ Requires: dmidecode %endif %if 0%{?suse_version} && 0%{?suse_version} <= 1210 %define initsys sysvinit_suse -Patch40: cloud-init-python26.patch %else %define initsys systemd BuildRequires: systemd @@ -187,52 +174,29 @@ according to the fetched configuration data from the admin node. Documentation and examples for cloud-init tools -%package test -Summary: Cloud node initialization tool - Testsuite -Group: System/Management -Requires: cloud-init = %{version} - -%description test -Cloud-init is an init script that initializes a cloud node (VM) -according to the fetched configuration data from the admin node. - -Unit tests for the cloud-init tools +#%package test +#Summary: Cloud node initialization tool - Testsuite +#Group: System/Management +#Requires: cloud-init = %{version} +# +#%description test +#Cloud-init is an init script that initializes a cloud node (VM) +#according to the fetched configuration data from the admin node. +# +#Unit tests for the cloud-init tools %prep %setup -q -%patch0 -p1 -%patch1 -p1 -%patch2 -%patch3 -%patch5 -%patch6 -%patch9 +%patch4 -p0 %patch10 -p1 -%patch11 %patch12 -%patch14 +%patch13 %patch20 -%patch21 -%patch22 -%patch23 -%patch24 -%patch25 -p1 -%patch26 -p1 %patch27 -%patch28 -p0 %patch29 -p0 -%patch30 -%patch32 -p1 -%patch33 -p1 %patch34 -%if 0%{?suse_version} && 0%{?suse_version} <= 1210 -%patch40 -p1 -%endif +%patch35 -p1 -%if 0%{?suse_version} <= 1130 -# disable ecdsa for SLE 11 (not available) -echo "ssh_genkeytypes: ['rsa', 'dsa']" >> %{SOURCE1} -%endif %build %if 0%{?suse_version} && 0%{?suse_version} <= 1315 @@ -247,16 +211,11 @@ python3 setup.py build # these tests are currently failing due to suse patches rm -v tests/unittests/test_distros/test_netconfig.py rm -v tests/unittests/test_net.py +# Ignore test failure currently not doing anything with opennebula rm -v tests/unittests/test_datasource/test_opennebula.py -rm -v tests/unittests/test_datasource/test_cloudstack.py -# These tests fail in python 3 -rm -v tests/unittests/test_datasource/test_altcloud.py -rm -v tests/unittests/test_handler/test_handler_apt_source_v3.py -%if 0%{?suse_version} && 0%{?suse_version} <= 1315 -python -m testtools.run -%else -python3 -m testtools.run -%endif +# To be investigated +rm -v tests/unittests/test_handler/test_handler_ntp.py +make test %endif @@ -275,7 +234,10 @@ mkdir -p %{buildroot}%{_localstatedir}/lib/cloud # move documentation mkdir -p %{buildroot}%{_defaultdocdir} mv %{buildroot}%{_datadir}/doc/%{name} %{buildroot}%{docdir} -cp -a %{SOURCE1} %{buildroot}/%{_sysconfdir}/cloud/cloud.cfg +%if 0%{?suse_version} <= 1130 +# disable ecdsa for SLE 11 (not available) +echo "ssh_genkeytypes: ['rsa', 'dsa']" >> %{buildroot}/%{_sysconfdir}/cloud/cloud.cfg +%endif # copy the LICENSE cp LICENSE %{buildroot}%{docdir} # Set the distribution indicator @@ -296,7 +258,7 @@ sed -i s/INSERT_SUSE_DISTRO/opensuse/ %{buildroot}/%{_sysconfdir}/cloud/cloud.cf %if 0%{?suse_version} && 0%{?suse_version} > 1110 mkdir -p %{buildroot}/%{_sysconfdir}/rsyslog.d mkdir -p %{buildroot}/usr/lib/udev/rules.d/ -cp -a %{SOURCE2} %{buildroot}/%{_sysconfdir}/rsyslog.d/21-cloudinit.conf +cp -a %{SOURCE1} %{buildroot}/%{_sysconfdir}/rsyslog.d/21-cloudinit.conf mv %{buildroot}/lib/udev/rules.d/66-azure-ephemeral.rules %{buildroot}/usr/lib/udev/rules.d/ %endif @@ -396,15 +358,11 @@ popd %{docdir}/examples/* %{docdir}/README %{docdir}/*.txt -%{docdir}/*.rst +#%{docdir}/*.rst %dir %{docdir}/examples -%files test -%defattr(-,root,root) -%if 0%{?suse_version} && 0%{?suse_version} <= 1315 -%{python_sitelib}/tests -%else -%{python3_sitelib}/tests -%endif +#%files test +#%defattr(-,root,root) +#%{python_sitelib}/tests %changelog diff --git a/cloud.cfg.suse b/cloud.cfg.suse deleted file mode 100644 index 904a0d4..0000000 --- a/cloud.cfg.suse +++ /dev/null @@ -1,61 +0,0 @@ -# Adapted default config for (open)SUSE systems - -users: - - root - -disable_root: false -preserve_hostname: false -syslog_fix_perms: root:root - -# The modules that run in the 'init' stage -cloud_init_modules: - - migrator - - bootcmd - - write-files - - growpart - - resizefs - - set_hostname - - update_hostname - - update_etc_hosts - - ca-certs - - rsyslog - - users-groups - - ssh - -# The modules that run in the 'config' stage -cloud_config_modules: - - mounts - - ssh-import-id - - locale - - set-passwords - - zypp_add_repo - - package-update-upgrade-install - - timezone - - puppet - - chef - - salt-minion - - mcollective - - disable-ec2-metadata - - runcmd - - byobu - -# The modules that run in the 'final' stage -cloud_final_modules: - - rightscale_userdata - - scripts-per-once - - scripts-per-boot - - scripts-per-instance - - scripts-user - - ssh-authkey-fingerprints - - keys-to-console - - phone-home - - final-message - - power-state-change - -# System and/or distro specific settings -system_info: - distro: INSERT_SUSE_DISTRO - paths: - cloud_dir: /var/lib/cloud/ - templates_dir: /etc/cloud/templates/ - ssh_svcname: sshd diff --git a/dataSourceOpenNebula.patch b/dataSourceOpenNebula.patch deleted file mode 100644 index b08cce9..0000000 --- a/dataSourceOpenNebula.patch +++ /dev/null @@ -1,98 +0,0 @@ ---- cloudinit/sources/DataSourceOpenNebula.py.orig -+++ cloudinit/sources/DataSourceOpenNebula.py -@@ -121,7 +121,7 @@ class BrokenContextDiskDir(Exception): - - class OpenNebulaNetwork(object): - REG_DEV_MAC = re.compile( -- r'^\d+: (eth\d+):.*?link\/ether (..:..:..:..:..:..) ?', -+ r'^\d+: (eth\d+):.*?link\/\W+ (..:..:..:..:..:..) ?', - re.MULTILINE | re.DOTALL) - - def __init__(self, ip, context): -@@ -130,12 +130,24 @@ class OpenNebulaNetwork(object): - self.ifaces = self.get_ifaces() - - def get_ifaces(self): -- return self.REG_DEV_MAC.findall(self.ip) -+ list = self.REG_DEV_MAC.findall(self.ip) -+ ifaces = dict() -+ for l in list: -+ ifaces[l[1]] = l[0] -+ return ifaces - - def mac2ip(self, mac): - components = mac.split(':')[2:] - return [str(int(c, 16)) for c in components] - -+ def get_context_interfaces(self): -+ -+ def device_mac(t): return re.match(r"ETH\d+_MAC", t) -+ -+ mac_vars = filter(device_mac, self.context.keys()) -+ context_interfaces = [v.split('_')[0] for v in mac_vars] -+ return context_interfaces -+ - def get_ip(self, dev, components): - var_name = dev.upper() + '_IP' - if var_name in self.context: -@@ -188,27 +200,39 @@ class OpenNebulaNetwork(object): - conf.append('iface lo inet loopback') - conf.append('') - -- for i in self.ifaces: -- dev = i[0] -- mac = i[1] -+ context_interfaces = self.get_context_interfaces() -+ -+ if len(context_interfaces): -+ try: -+ (out, _err) = util.subp(["systemctl", "stop", "NetworkManager"]) -+ (out, _err) = util.subp(["systemctl", "disable", "NetworkManager"]) -+ except util.ProcessExecutionError: -+ util.logexc(LOG, "Disable NetworkManager command failed") -+ -+ for interface in context_interfaces: -+ mac = self.context[interface+"_MAC"] -+ dev = self.ifaces.get(mac) -+ if dev is None: -+ continue -+ - ip_components = self.mac2ip(mac) - - conf.append('auto ' + dev) - conf.append('iface ' + dev + ' inet static') -- conf.append(' address ' + self.get_ip(dev, ip_components)) -- conf.append(' network ' + self.get_network(dev, ip_components)) -- conf.append(' netmask ' + self.get_mask(dev)) -+ conf.append(' address ' + self.get_ip(nterface, ip_components)) -+ conf.append(' network ' + self.get_network(interface, ip_components)) -+ conf.append(' netmask ' + self.get_mask(interface)) - -- gateway = self.get_gateway(dev) -+ gateway = self.get_gateway(interface) - if gateway: - conf.append(' gateway ' + gateway) - -- domain = self.get_domain(dev) -+ domain = self.get_domain(interface) - if domain: - conf.append(' dns-search ' + domain) - - # add global DNS servers to all interfaces -- dns = self.get_dns(dev) -+ dns = self.get_dns(interface) - if global_dns or dns: - all_dns = global_dns - if dns: -@@ -375,9 +399,8 @@ def read_context_disk_dir(source_dir, as - - if ssh_key_var: - lines = context.get(ssh_key_var).splitlines() -- results['metadata']['public-keys'] = [l for l in lines -- if len(l) and not -- l.startswith("#")] -+ ssh_keys = [l for l in lines if len(l) and not l.startswith("#")] -+ results['metadata']['public-keys'] = ssh_keys - - # custom hostname -- try hostname or leave cloud-init - # itself create hostname from IP address later diff --git a/dynamicInitCmd.diff b/dynamicInitCmd.diff deleted file mode 100644 index a5e0209..0000000 --- a/dynamicInitCmd.diff +++ /dev/null @@ -1,28 +0,0 @@ -=== modified file 'cloudinit/config/cc_set_passwords.py' -Index: cloudinit/config/cc_set_passwords.py -=================================================================== ---- cloudinit/config/cc_set_passwords.py.orig -+++ cloudinit/config/cc_set_passwords.py -@@ -147,7 +147,7 @@ def handle(_name, cfg, cloud, log, args) - util.write_file(ssh_util.DEF_SSHD_CFG, "\n".join(lines)) - - try: -- cmd = cloud.distro.init_cmd # Default service -+ cmd = cloud.distro.get_init_cmd() - cmd.append(cloud.distro.get_option('ssh_svcname', 'ssh')) - cmd.append('restart') - if 'systemctl' in cmd: # Switch action ordering -Index: cloudinit/distros/__init__.py -=================================================================== ---- cloudinit/distros/__init__.py.orig -+++ cloudinit/distros/__init__.py -@@ -88,6 +88,9 @@ class Distro(object): - " no file found at %s") % (tz, tz_file)) - return tz_file - -+ def get_init_cmd(self): -+ return self.init_cmd -+ - def get_option(self, opt_name, default=None): - return self._cfg.get(opt_name, default) - diff --git a/fix-default-systemd-unit-dir.patch b/fix-default-systemd-unit-dir.patch index 78822af..ed46a2e 100644 --- a/fix-default-systemd-unit-dir.patch +++ b/fix-default-systemd-unit-dir.patch @@ -1,6 +1,6 @@ --- setup.py.orig +++ setup.py -@@ -58,8 +58,8 @@ def tiny_p(cmd, capture=True): +@@ -49,8 +49,8 @@ def tiny_p(cmd, capture=True): def pkg_config_read(library, var): fallbacks = { 'systemd': { @@ -13,9 +13,9 @@ cmd = ['pkg-config', '--variable=%s' % var, library] --- systemd/cloud-init-generator.orig +++ systemd/cloud-init-generator -@@ -7,7 +7,7 @@ LOG_D="/run/cloud-init" - ENABLE="enabled" - DISABLE="disabled" +@@ -9,7 +9,7 @@ DISABLE="disabled" + FOUND="found" + NOTFOUND="notfound" RUN_ENABLED_FILE="$LOG_D/$ENABLE" -CLOUD_SYSTEM_TARGET="/lib/systemd/system/cloud-init.target" +CLOUD_SYSTEM_TARGET="/usr/lib/systemd/system/cloud-init.target" diff --git a/openSUSEhostsTemplate.diff b/openSUSEhostsTemplate.diff deleted file mode 100644 index 4d75e86..0000000 --- a/openSUSEhostsTemplate.diff +++ /dev/null @@ -1,29 +0,0 @@ ---- /dev/null -+++ templates/hosts.opensuse.tmpl -@@ -0,0 +1,26 @@ -+* -+ This file /etc/cloud/templates/hosts.opensuse.tmpl is only utilized -+ if enabled in cloud-config. Specifically, in order to enable it -+ you need to add the following to config: -+ manage_etc_hosts: True -+*# -+# Your system has configured 'manage_etc_hosts' as True. -+# As a result, if you wish for changes to this file to persist -+# then you will need to either -+# a.) make changes to the master file in -+# /etc/cloud/templates/hosts.opensuse.tmpl -+# b.) change or remove the value of 'manage_etc_hosts' in -+# /etc/cloud/cloud.cfg or cloud-config from user-data -+# -+# The following lines are desirable for IPv4 capable hosts -+127.0.0.1 localhost -+ -+# The following lines are desirable for IPv6 capable hosts -+::1 localhost ipv6-localhost ipv6-loopback -+fe00::0 ipv6-localnet -+ -+ff00::0 ipv6-mcastprefix -+ff02::1 ipv6-allnodes -+ff02::2 ipv6-allrouters -+ff02::3 ipv6-allhosts -+ diff --git a/setupSUSEsysVInit.diff b/setupSUSEsysVInit.diff deleted file mode 100644 index 702e247..0000000 --- a/setupSUSEsysVInit.diff +++ /dev/null @@ -1,18 +0,0 @@ ---- setup.py.orig -+++ setup.py -@@ -75,6 +75,7 @@ INITSYS_FILES = { - 'sysvinit_freebsd': [f for f in glob('sysvinit/freebsd/*') if is_f(f)], - 'sysvinit_deb': [f for f in glob('sysvinit/debian/*') if is_f(f)], - 'sysvinit_openrc': [f for f in glob('sysvinit/gentoo/*') if is_f(f)], -+ 'sysvinit_suse': [f for f in glob('sysvinit/suse/*') if is_f(f)], - 'systemd': [f for f in (glob('systemd/*.service') + - glob('systemd/*.target')) if is_f(f)], - 'systemd.generators': [f for f in glob('systemd/*-generator') if is_f(f)], -@@ -85,6 +86,7 @@ INITSYS_ROOTS = { - 'sysvinit_freebsd': '/usr/local/etc/rc.d', - 'sysvinit_deb': '/etc/init.d', - 'sysvinit_openrc': '/etc/init.d', -+ 'sysvinit_suse': '/etc/init.d', - 'systemd': pkg_config_read('systemd', 'systemdsystemunitdir'), - 'systemd.generators': pkg_config_read('systemd', - 'systemdsystemgeneratordir'), diff --git a/skip-argparse-on-python3.patch b/skip-argparse-on-python3.patch deleted file mode 100644 index e61ed14..0000000 --- a/skip-argparse-on-python3.patch +++ /dev/null @@ -1,23 +0,0 @@ ---- requirements.txt -+++ requirements.txt -@@ -27,9 +27,6 @@ - # All new style configurations are in the yaml format - pyyaml - --# The new main entrypoint uses argparse instead of optparse --argparse -- - # Requests handles ssl correctly! - requests - ---- setup.py -+++ setup.py -@@ -198,7 +198,7 @@ - - requirements = read_requires() - if sys.version_info < (3,): -- requirements.append('cheetah') -+ requirements.extend(('cheetah', 'argparse')) - - setuptools.setup( - name='cloud-init', diff --git a/suseIntegratedHandler.patch b/suseIntegratedHandler.patch deleted file mode 100644 index adc0693..0000000 --- a/suseIntegratedHandler.patch +++ /dev/null @@ -1,421 +0,0 @@ -Index: cloudinit/distros/opensuse.py -=================================================================== ---- /dev/null -+++ cloudinit/distros/opensuse.py -@@ -0,0 +1,233 @@ -+# vi: ts=4 expandtab -+# -+# Copyright (C) 2016 SUSE LLC -+# Copyright (C) 2013 Hewlett-Packard Development Company, L.P. -+# -+# Author: Robert Schweikert -+# Author: Juerg Haefliger -+# -+# Leaning very heavily on the RHEL and Debian implementation -+# -+# This program is free software: you can redistribute it and/or modify -+# it under the terms of the GNU General Public License version 3, as -+# published by the Free Software Foundation. -+# -+# This program is distributed in the hope that it will be useful, -+# but WITHOUT ANY WARRANTY; without even the implied warranty of -+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -+# GNU General Public License for more details. -+# -+# You should have received a copy of the GNU General Public License -+# along with this program. If not, see . -+ -+from cloudinit import distros -+ -+from cloudinit.distros.parsers.hostname import HostnameConf -+ -+from cloudinit import helpers -+from cloudinit import log as logging -+from cloudinit import util -+ -+from cloudinit.distros import net_util -+from cloudinit.distros import rhel_util as rhutil -+#from cloudinit.net import sysconfig -+#from cloudinit.net.network_state import parse_net_config_data -+from cloudinit.settings import PER_INSTANCE -+ -+LOG = logging.getLogger(__name__) -+ -+class Distro(distros.Distro): -+ clock_conf_fn = '/etc/sysconfig/clock' -+ hostname_conf_fn = '/etc/HOSTNAME' -+ init_cmd = ['service'] -+ locale_conf_fn = '/etc/sysconfig/language' -+ network_conf_fn = '/etc/sysconfig/network' -+ network_script_tpl = '/etc/sysconfig/network/ifcfg-%s' -+ resolve_conf_fn = '/etc/resolv.conf' -+ route_conf_tpl = '/etc/sysconfig/network/ifroute-%s' -+ systemd_hostname_conf_fn = '/etc/hostname' -+ systemd_locale_conf_fn = '/etc/locale.conf' -+ tz_local_fn = '/etc/localtime' -+ -+ def __init__(self, name, cfg, paths): -+ distros.Distro.__init__(self, name, cfg, paths) -+# self._net_renderer = sysconfig.Renderer() -+ # This will be used to restrict certain -+ # calls from repeatly happening (when they -+ # should only happen say once per instance...) -+ self._runner = helpers.Runners(paths) -+ self.osfamily = 'suse' -+ cfg['ssh_svcname'] = 'sshd' -+ self.systemdDist = util.which('systemctl') -+ if self.systemdDist: -+ self.init_cmd = ['systemctl'] -+ cfg['ssh_svcname'] = 'sshd.service' -+ -+ def apply_locale(self, locale, out_fn=None): -+ if self.systemdDist: -+ if not out_fn: -+ out_fn = self.systemd_locale_conf_fn -+ locale_cfg = {'LANG': locale} -+ else: -+ if not out_fn: -+ out_fn = self.locale_conf_fn -+ locale_cfg = {'RC_LANG': locale} -+ rhutil.update_sysconfig_file(out_fn, locale_cfg) -+ -+ def install_packages(self, pkglist): -+ self.package_command('install', args='-l', pkgs=pkglist) -+ -+ def package_command(self, command, args=None, pkgs=None): -+ if pkgs is None: -+ pkgs = [] -+ -+ cmd = ['zypper'] -+ # No user interaction possible, enable non-interactive mode -+ cmd.append('--non-interactive') -+ -+ # Comand is the operation, such as install -+ if command == 'upgrade': -+ command = 'update' -+ cmd.append(command) -+ -+ # args are the arguments to the command, not global options -+ if args and isinstance(args, str): -+ cmd.append(args) -+ elif args and isinstance(args, list): -+ cmd.extend(args) -+ -+ pkglist = util.expand_package_list('%s-%s', pkgs) -+ cmd.extend(pkglist) -+ -+ # Allow the output of this to flow outwards (ie not be captured) -+ util.subp(cmd, capture=False) -+ -+ def set_timezone(self, tz): -+ tz_file = self._find_tz_file(tz) -+ if self.systemdDist: -+ # Currently, timedatectl complains if invoked during startup -+ # so for compatibility, create the link manually. -+ util.del_file(self.tz_local_fn) -+ util.sym_link(tz_file, self.tz_local_fn) -+ else: -+ # Adjust the sysconfig clock zone setting -+ clock_cfg = { -+ 'TIMEZONE': str(tz), -+ } -+ rhutil.update_sysconfig_file(self.clock_conf_fn, clock_cfg) -+ # This ensures that the correct tz will be used for the system -+ util.copy(tz_file, self.tz_local_fn) -+ -+ def update_package_sources(self): -+ self._runner.run("update-sources", self.package_command, -+ ['refresh'], freq=PER_INSTANCE) -+ -+ -+ def _bring_up_interfaces(self, device_names): -+ if device_names and 'all' in device_names: -+ raise RuntimeError(('Distro %s can not translate ' -+ 'the device name "all"') % (self.name)) -+ return distros.Distro._bring_up_interfaces(self, device_names) -+ -+ -+ def _read_hostname(self, filename, default=None): -+ if self.systemdDist and filename.endswith('/previous-hostname'): -+ return util.load_file(filename).strip() -+ elif self.systemdDist: -+ (out, _err) = util.subp(['hostname']) -+ if len(out): -+ return out -+ else: -+ return default -+ else: -+ try: -+ conf = self._read_hostname_conf(filename) -+ hostname = conf.hostname -+ except IOError: -+ pass -+ if not hostname: -+ return default -+ return hostname -+ -+ def _read_hostname_conf(self, filename): -+ conf = HostnameConf(util.load_file(filename)) -+ conf.parse() -+ return conf -+ -+ def _read_system_hostname(self): -+ if self.systemdDist: -+ host_fn = self.systemd_hostname_conf_fn -+ else: -+ host_fn = self.hostname_conf_fn -+ return (host_fn, self._read_hostname(host_fn)) -+ -+ def _write_hostname(self, hostname, out_fn): -+ if self.systemdDist and out_fn.endswith('/previous-hostname'): -+ util.write_file(out_fn, hostname) -+ elif self.systemdDist: -+ util.subp(['hostnamectl', 'set-hostname', str(hostname)]) -+ else: -+ conf = None -+ try: -+ # Try to update the previous one -+ # so lets see if we can read it first. -+ conf = self._read_hostname_conf(out_fn) -+ except IOError: -+ pass -+ if not conf: -+ conf = HostnameConf('') -+ conf.set_hostname(hostname) -+ util.write_file(out_fn, str(conf), 0o644) -+ -+ def _write_network(self, settings): -+ # Convert debian settings to ifcfg format -+ entries = net_util.translate_network(settings) -+ LOG.debug("Translated ubuntu style network settings %s into %s", -+ settings, entries) -+ # Make the intermediate format as the suse format... -+ nameservers = [] -+ searchservers = [] -+ dev_names = entries.keys() -+ for (dev, info) in entries.items(): -+ net_fn = self.network_script_tpl % (dev) -+ route_fn = self.route_conf_tpl % (dev) -+ mode = None -+ if info.get('auto', None): -+ mode = 'auto' -+ else: -+ mode = 'manual' -+ bootproto = info.get('bootproto', None) -+ gateway = info.get('gateway', None) -+ net_cfg = { -+ 'BOOTPROTO': bootproto, -+ 'BROADCAST': info.get('broadcast'), -+ 'GATEWAY': gateway, -+ 'IPADDR': info.get('address'), -+ 'LLADDR': info.get('hwaddress'), -+ 'NETMASK': info.get('netmask'), -+ 'STARTMODE': mode, -+ 'USERCONTROL': 'no' -+ } -+ if dev != 'lo': -+ net_cfg['ETHTOOL_OPTIONS'] = '' -+ else: -+ net_cfg['FIREWALL'] = 'no' -+ rhutil.update_sysconfig_file(net_fn, net_cfg, True) -+ if gateway and bootproto == 'static': -+ default_route = 'default %s' %gateway -+ util.write_file(route_fn, default_route, 0o644) -+ if 'dns-nameservers' in info: -+ nameservers.extend(info['dns-nameservers']) -+ if 'dns-search' in info: -+ searchservers.extend(info['dns-search']) -+ if nameservers or searchservers: -+ rhutil.update_resolve_conf_file(self.resolve_conf_fn, -+ nameservers, searchservers) -+ return dev_names -+ -+# New interface cannot yet be implemented/used as we have to figure out -+# how to have a distro specific renderer -+# def _write_network_config(self, netconfig): -+# ns = parse_net_config_data(netconfig) -+# self._net_renderer.render_network_state("/", ns) -+# return [] -Index: cloudinit/distros/sles.py -=================================================================== ---- cloudinit/distros/sles.py.orig -+++ cloudinit/distros/sles.py -@@ -1,10 +1,9 @@ - # vi: ts=4 expandtab - # --# Copyright (C) 2013 Hewlett-Packard Development Company, L.P. --# --# Author: Juerg Haefliger -+# Copyright (C) 2014 SUSE LLC - # - # Leaning very heavily on the RHEL and Debian implementation -+# Author: Robert Schweikert - # - # This program is free software: you can redistribute it and/or modify - # it under the terms of the GNU General Public License version 3, as -@@ -18,162 +17,12 @@ - # You should have received a copy of the GNU General Public License - # along with this program. If not, see . - --from cloudinit import distros -- --from cloudinit.distros.parsers.hostname import HostnameConf -+from cloudinit.distros import opensuse - --from cloudinit import helpers - from cloudinit import log as logging --from cloudinit import util -- --from cloudinit.distros import net_util --from cloudinit.distros import rhel_util --from cloudinit.settings import PER_INSTANCE - - LOG = logging.getLogger(__name__) - -+class Distro(opensuse.Distro): -+ pass - --class Distro(distros.Distro): -- clock_conf_fn = '/etc/sysconfig/clock' -- locale_conf_fn = '/etc/sysconfig/language' -- network_conf_fn = '/etc/sysconfig/network' -- hostname_conf_fn = '/etc/HOSTNAME' -- network_script_tpl = '/etc/sysconfig/network/ifcfg-%s' -- resolve_conf_fn = '/etc/resolv.conf' -- tz_local_fn = '/etc/localtime' -- -- def __init__(self, name, cfg, paths): -- distros.Distro.__init__(self, name, cfg, paths) -- # This will be used to restrict certain -- # calls from repeatly happening (when they -- # should only happen say once per instance...) -- self._runner = helpers.Runners(paths) -- self.osfamily = 'suse' -- -- def install_packages(self, pkglist): -- self.package_command('install', args='-l', pkgs=pkglist) -- -- def _write_network(self, settings): -- # Convert debian settings to ifcfg format -- entries = net_util.translate_network(settings) -- LOG.debug("Translated ubuntu style network settings %s into %s", -- settings, entries) -- # Make the intermediate format as the suse format... -- nameservers = [] -- searchservers = [] -- dev_names = entries.keys() -- for (dev, info) in entries.items(): -- net_fn = self.network_script_tpl % (dev) -- mode = info.get('auto') -- if mode and mode.lower() == 'true': -- mode = 'auto' -- else: -- mode = 'manual' -- net_cfg = { -- 'BOOTPROTO': info.get('bootproto'), -- 'BROADCAST': info.get('broadcast'), -- 'GATEWAY': info.get('gateway'), -- 'IPADDR': info.get('address'), -- 'LLADDR': info.get('hwaddress'), -- 'NETMASK': info.get('netmask'), -- 'STARTMODE': mode, -- 'USERCONTROL': 'no' -- } -- if dev != 'lo': -- net_cfg['ETHERDEVICE'] = dev -- net_cfg['ETHTOOL_OPTIONS'] = '' -- else: -- net_cfg['FIREWALL'] = 'no' -- rhel_util.update_sysconfig_file(net_fn, net_cfg, True) -- if 'dns-nameservers' in info: -- nameservers.extend(info['dns-nameservers']) -- if 'dns-search' in info: -- searchservers.extend(info['dns-search']) -- if nameservers or searchservers: -- rhel_util.update_resolve_conf_file(self.resolve_conf_fn, -- nameservers, searchservers) -- return dev_names -- -- def apply_locale(self, locale, out_fn=None): -- if not out_fn: -- out_fn = self.locale_conf_fn -- locale_cfg = { -- 'RC_LANG': locale, -- } -- rhel_util.update_sysconfig_file(out_fn, locale_cfg) -- -- def _write_hostname(self, hostname, out_fn): -- conf = None -- try: -- # Try to update the previous one -- # so lets see if we can read it first. -- conf = self._read_hostname_conf(out_fn) -- except IOError: -- pass -- if not conf: -- conf = HostnameConf('') -- conf.set_hostname(hostname) -- util.write_file(out_fn, str(conf), 0o644) -- -- def _read_system_hostname(self): -- host_fn = self.hostname_conf_fn -- return (host_fn, self._read_hostname(host_fn)) -- -- def _read_hostname_conf(self, filename): -- conf = HostnameConf(util.load_file(filename)) -- conf.parse() -- return conf -- -- def _read_hostname(self, filename, default=None): -- hostname = None -- try: -- conf = self._read_hostname_conf(filename) -- hostname = conf.hostname -- except IOError: -- pass -- if not hostname: -- return default -- return hostname -- -- def _bring_up_interfaces(self, device_names): -- if device_names and 'all' in device_names: -- raise RuntimeError(('Distro %s can not translate ' -- 'the device name "all"') % (self.name)) -- return distros.Distro._bring_up_interfaces(self, device_names) -- -- def set_timezone(self, tz): -- tz_file = self._find_tz_file(tz) -- # Adjust the sysconfig clock zone setting -- clock_cfg = { -- 'TIMEZONE': str(tz), -- } -- rhel_util.update_sysconfig_file(self.clock_conf_fn, clock_cfg) -- # This ensures that the correct tz will be used for the system -- util.copy(tz_file, self.tz_local_fn) -- -- def package_command(self, command, args=None, pkgs=None): -- if pkgs is None: -- pkgs = [] -- -- cmd = ['zypper'] -- # No user interaction possible, enable non-interactive mode -- cmd.append('--non-interactive') -- -- # Comand is the operation, such as install -- cmd.append(command) -- -- # args are the arguments to the command, not global options -- if args and isinstance(args, str): -- cmd.append(args) -- elif args and isinstance(args, list): -- cmd.extend(args) -- -- pkglist = util.expand_package_list('%s-%s', pkgs) -- cmd.extend(pkglist) -- -- # Allow the output of this to flow outwards (ie not be captured) -- util.subp(cmd, capture=False) -- -- def update_package_sources(self): -- self._runner.run("update-sources", self.package_command, -- ['refresh'], freq=PER_INSTANCE) diff --git a/zypp_add_repos.diff b/zypp_add_repos.diff index 0606c17..7cfa83b 100644 --- a/zypp_add_repos.diff +++ b/zypp_add_repos.diff @@ -1,39 +1,110 @@ ---- cloudinit/config/cc_zypp_add_repo.py -+++ cloudinit/config/cc_zypp_add_repo.py 2016/11/23 13:10:49 -@@ -0,0 +1,112 @@ -+# vi: ts=4 expandtab +Index: cloudinit/config/cc_zypp_add_repo.py +=================================================================== +--- /dev/null ++++ cloudinit/config/cc_zypper_add_repo.py +@@ -0,0 +1,220 @@ +# -+# Copyright (C) 2016 SUSE LLC. -+# This file is based on cc_yum_add_repo.py and was rewritten by -+# Thorsten Kukuk for zypp repos. ++# Copyright (C) 2017 SUSE LLC. +# -+# Original file was: -+# -+# Copyright (C) 2012 Yahoo! Inc. -+# -+# Author: Joshua Harlow -+# -+# This program is free software: you can redistribute it and/or modify -+# it under the terms of the GNU General Public License version 3, as -+# published by the Free Software Foundation. -+# -+# This program is distributed in the hope that it will be useful, -+# but WITHOUT ANY WARRANTY; without even the implied warranty of -+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -+# GNU General Public License for more details. -+# -+# You should have received a copy of the GNU General Public License -+# along with this program. If not, see . ++# This file is part of cloud-init. See LICENSE file for license information. ++ ++"""zypper_add_repo: Add zyper repositories to the system""" + -+import os + +import configobj -+import six ++import os + ++ ++from cloudinit import log as logging +from cloudinit import util ++from cloudinit.config.schema import get_schema_doc ++from cloudinit.settings import PER_ALWAYS ++from six import string_types ++from textwrap import dedent + +distros = ['opensuse', 'sles'] + ++schema = { ++ 'id': 'cc_zypper_add_repo', ++ 'name': 'ZypperAddRepo', ++ 'title': 'Configure zypper behavior and add zypper repositories', ++ 'description': dedent("""\ ++ Configure zypper behavior by modifying /etc/zypp/zypp.conf. The ++ configuration writer is "dumb" and will simply append the provided ++ configuration options to the configuration file. Option settings ++ that may be duplicate will be resolved by the way the zypp.conf file ++ is parsed. The file is in INI format. ++ Add repositories to the system. No validation is performed on the ++ repository file entries, it is assumed the user is familiar with ++ the zypper repository file format."""), ++ 'distros': distros, ++ 'examples': [dedent("""\ ++ zypper: ++ repos: ++ - id: opensuse-oss ++ name: os-oss ++ baseurl: http://dl.opensuse.org/dist/leap/v/repo/oss/ ++ enabled: 1 ++ autorefresh: 1 ++ - id: opensuse-oss-update ++ name: os-oss-up ++ baseurl: http://dl.opensuse.org/dist/leap/v/update ++ # any setting per ++ # https://en.opensuse.org/openSUSE:Standards_RepoInfo ++ # enable and autorefresh are on by default ++ config: ++ reposdir: /etc/zypp/repos.dir ++ servicesdir: /etc/zypp/services.d ++ download.use_deltarpm: true ++ # any setting in /etc/zypp/zypp.conf ++ """)], ++ 'frequency': PER_ALWAYS, ++ 'type': 'object', ++ 'properties': { ++ 'zypper': { ++ 'type': 'object', ++ 'properties': { ++ 'repos': { ++ 'type': 'array', ++ 'items': { ++ 'type': 'object', ++ 'properties': { ++ 'id': { ++ 'type': 'string', ++ 'description': dedent("""\ ++ The unique id of the repo, used when ++ writing ++ /etc/zypp/repos.d/.repo.""") ++ }, ++ 'baseurl': { ++ 'type': 'string', ++ 'format': 'uri', # built-in format type ++ 'description': 'The base repositoy URL' ++ } ++ }, ++ 'required': ['id', 'baseurl'], ++ 'additionalProperties': True ++ }, ++ 'minItems': 1 ++ }, ++ 'config': { ++ 'type': 'object', ++ 'description': dedent("""\ ++ Any supported zypo.conf key is written to ++ /etc/zypp/zypp.conf'""") ++ } ++ }, ++ 'required': [], ++ 'minProperties': 1, # Either config or repo must be provided ++ 'additionalProperties': False, # only repos and config allowed ++ } ++ } ++} ++ ++__doc__ = get_schema_doc(schema) # Supplement python help() ++ ++LOG = logging.getLogger(__name__) ++ + +def _canonicalize_id(repo_id): + repo_id = repo_id.replace(" ", "_") @@ -41,12 +112,12 @@ + + +def _format_repo_value(val): -+ if isinstance(val, (bool)): ++ if isinstance(val, bool): + # zypp prefers 1/0 -+ return str(int(val)) ++ return 1 if val else 0 + if isinstance(val, (list, tuple)): + return "\n ".join([_format_repo_value(v) for v in val]) -+ if not isinstance(val, six.string_types): ++ if not isinstance(val, string_types): + return str(val) + return val + @@ -63,58 +134,99 @@ + return "\n".join(lines) + + -+def handle(name, cfg, _cloud, log, _args): -+ repos = cfg.get('zypp_repos') ++def _write_repos(repos, repo_base_path): ++ """Write the user-provided repo definition files ++ @param repos: A list of repo dictionary objects provided by the user's ++ cloud config. ++ @param repo_base_path: The directory path to which repo definitions are ++ written. ++ """ ++ + if not repos: -+ log.debug(("Skipping module named %s," -+ " no 'zypp_repos' configuration found"), name) + return -+ repo_base_path = util.get_cfg_option_str(cfg, 'zypp_repo_dir', -+ '/etc/zypp/repos.d/') -+ repo_locations = {} -+ repo_configs = {} -+ for (repo_id, repo_config) in repos.items(): ++ valid_repos = {} ++ for index, user_repo_config in enumerate(repos): ++ # Skip on absent required keys ++ missing_keys = set(['id', 'baseurl']).difference(set(user_repo_config)) ++ if missing_keys: ++ LOG.warning( ++ "Repo config at index %d is missing required config keys: %s", ++ index, ",".join(missing_keys)) ++ continue ++ repo_id = user_repo_config.get('id') + canon_repo_id = _canonicalize_id(repo_id) + repo_fn_pth = os.path.join(repo_base_path, "%s.repo" % (canon_repo_id)) + if os.path.exists(repo_fn_pth): -+ log.info("Skipping repo %s, file %s already exists!", ++ LOG.info("Skipping repo %s, file %s already exists!", + repo_id, repo_fn_pth) + continue -+ elif repo_id in repo_locations: -+ log.info("Skipping repo %s, file %s already pending!", ++ elif repo_id in valid_repos: ++ LOG.info("Skipping repo %s, file %s already pending!", + repo_id, repo_fn_pth) + continue -+ if not repo_config: -+ repo_config = {} -+ # Do some basic sanity checks/cleaning -+ n_repo_config = {} -+ for (k, v) in repo_config.items(): -+ k = k.lower().strip().replace("-", "_") -+ if k: -+ n_repo_config[k] = v -+ repo_config = n_repo_config -+ missing_required = 0 -+ for req_field in ['baseurl']: -+ if req_field not in repo_config: -+ log.warn(("Repository %s does not contain a %s" -+ " configuration 'required' entry"), -+ repo_id, req_field) -+ missing_required += 1 ++ ++ # Do some basic key formatting ++ repo_config = dict( ++ (k.lower().strip().replace("-", "_"), v) ++ for k, v in user_repo_config.items() ++ if k and k != 'id') ++ ++ # Set defaults if not present + for field in ['enabled', 'autorefresh']: + if field not in repo_config: + repo_config[field] = '1' -+ if not missing_required: -+ repo_configs[repo_id] = repo_config -+ repo_locations[repo_id] = repo_fn_pth -+ else: -+ log.warn("Repository %s is missing %s required fields, skipping!", -+ repo_id, missing_required) -+ for (c_repo_id, path) in repo_locations.items(): -+ repo_blob = _format_repository_config(c_repo_id, -+ repo_configs.get(c_repo_id)) -+ util.write_file(path, repo_blob) ---- doc/examples/cloud-config-zypp-repo.txt -+++ doc/examples/cloud-config-zypp-repo.txt 2016/11/23 12:59:42 ++ ++ valid_repos[repo_id] = (repo_fn_pth, repo_config) ++ ++ for (repo_id, repo_data) in valid_repos.items(): ++ repo_blob = _format_repository_config(repo_id, repo_data[-1]) ++ util.write_file(repo_data[0], repo_blob) ++ ++ ++def _write_zypp_config(zypper_config): ++ """Write to the default zypp configuration file /etc/zypp/zypp.conf""" ++ if not zypper_config: ++ return ++ zypp_config = '/etc/zypp/zypp.conf' ++ zypp_conf_content = util.load_file(zypp_config) ++ new_settings = ['# Added via cloud.cfg'] ++ for setting, value in zypper_config.items(): ++ if setting == 'configdir': ++ msg = 'Changing the location of the zypper configuration is ' ++ msg += 'not supported, skipping "configdir" setting' ++ LOG.warning(msg) ++ continue ++ if value: ++ new_settings.append('%s=%s' % (setting, value)) ++ if len(new_settings) > 1: ++ new_config = zypp_conf_content + '\n'.join(new_settings) ++ else: ++ new_config = zypp_conf_content ++ util.write_file(zypp_config, new_config) ++ ++ ++def handle(name, cfg, _cloud, log, _args): ++ zypper_section = cfg.get('zypper') ++ if not zypper_section: ++ LOG.debug(("Skipping module named %s," ++ " no 'zypper' relevant configuration found"), name) ++ return ++ repos = zypper_section.get('repos') ++ if not repos: ++ LOG.debug(("Skipping module named %s," ++ " no 'repos' configuration found"), name) ++ return ++ zypper_config = zypper_section.get('config', {}) ++ repo_base_path = zypper_config.get('reposdir', '/etc/zypp/repos.d/') ++ ++ _write_zypp_config(zypper_config) ++ _write_repos(repos, repo_base_path) ++ ++# vi: ts=4 expandtab +Index: doc/examples/cloud-config-zypp-repo.txt +=================================================================== +--- /dev/null ++++ doc/examples/cloud-config-zypp-repo.txt @@ -0,0 +1,18 @@ +#cloud-config +# vim: syntax=yaml