- 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 OBS-URL: https://build.opensuse.org/package/show/Cloud:Tools/cloud-init?expand=0&rev=88
This commit is contained in:
parent
40312f95d8
commit
738f268769
@ -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'],
|
||||
}
|
||||
|
@ -1,3 +0,0 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:4a4f1f7fb9dd0987a02aa7cd6f609910294fce8f9724dcebc0cd88630b4f1fd6
|
||||
size 508777
|
3
cloud-init-17.1.tar.gz
Normal file
3
cloud-init-17.1.tar.gz
Normal file
@ -0,0 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:80f3bf5e8f57b67ac599aba2856568aeb30bd25187c7a363bed157a1e4d63e01
|
||||
size 780532
|
40
cloud-init-def-config.patch
Normal file
40
cloud-init-def-config.patch
Normal file
@ -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 %}
|
@ -1,23 +0,0 @@
|
||||
From 7ae201166402fbf2e6c1632028be956a954835ef Mon Sep 17 00:00:00 2001
|
||||
From: Scott Moser <smoser@brickies.net>
|
||||
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
|
||||
|
@ -1,782 +0,0 @@
|
||||
From 9f83bb8e80806d3dd79ba426474dc3c696e19a41 Mon Sep 17 00:00:00 2001
|
||||
From: Ben Howard <bh@digitalocean.com>
|
||||
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 <bh@digitalocean.com>
|
||||
+
|
||||
+# 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 <http://www.gnu.org/licenses/>.
|
||||
+
|
||||
+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
|
||||
|
15
cloud-init-final-no-apt.patch
Normal file
15
cloud-init-final-no-apt.patch
Normal file
@ -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
|
@ -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
|
@ -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)
|
||||
|
@ -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,
|
@ -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)
|
||||
|
10
cloud-init-more-tasks.patch
Normal file
10
cloud-init-more-tasks.patch
Normal file
@ -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
|
@ -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:
|
@ -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
|
@ -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
|
@ -1,14 +1,14 @@
|
||||
--- 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,
|
||||
|
@ -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)
|
@ -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
|
||||
|
11
cloud-init-set-variant.patch
Normal file
11
cloud-init-set-variant.patch
Normal file
@ -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"):
|
@ -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)
|
@ -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
|
||||
|
20
cloud-init-tests-set-exec.patch
Normal file
20
cloud-init-tests-set-exec.patch
Normal file
@ -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
|
||||
|
@ -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-<distro> 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
|
||||
|
||||
|
150
cloud-init.spec
150
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
|
||||
|
@ -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
|
@ -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
|
@ -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)
|
||||
|
@ -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"
|
||||
|
@ -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
|
||||
+
|
@ -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'),
|
@ -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',
|
@ -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 <rjschwei@suse.com>
|
||||
+# Author: Juerg Haefliger <juerg.haefliger@hp.com>
|
||||
+#
|
||||
+# 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 <http://www.gnu.org/licenses/>.
|
||||
+
|
||||
+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 <juerg.haefliger@hp.com>
|
||||
+# Copyright (C) 2014 SUSE LLC
|
||||
#
|
||||
# Leaning very heavily on the RHEL and Debian implementation
|
||||
+# Author: Robert Schweikert <rjschwei@suse.com>
|
||||
#
|
||||
# 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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
-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)
|
@ -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 <kukuk@suse.com> for zypp repos.
|
||||
+# Copyright (C) 2017 SUSE LLC.
|
||||
+#
|
||||
+# Original file was:
|
||||
+#
|
||||
+# Copyright (C) 2012 Yahoo! Inc.
|
||||
+#
|
||||
+# Author: Joshua Harlow <harlowja@yahoo-inc.com>
|
||||
+#
|
||||
+# 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 <http://www.gnu.org/licenses/>.
|
||||
+# 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/<id>.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
|
||||
+
|
||||
+ 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:
|
||||
+ 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
|
||||
+ 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
|
||||
|
Loading…
Reference in New Issue
Block a user