summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorScott Moser <smoser@brickies.net>2016-08-23 16:48:37 -0400
committerScott Moser <smoser@brickies.net>2016-08-23 16:48:37 -0400
commita5c3d4212f33c41a24d33e80e1871b6494e66fd5 (patch)
tree2836c9dab2d89fcbbad094e9935bb4417d05b238
parentaa2bbfdace36d1b0d48c630e95c4f5a803462f3b (diff)
downloadcloud-init-git-ubuntu/precise.tar.gz
Import version 0.6.3-0ubuntu1.25ubuntu/0.6.3-0ubuntu1.25ubuntu/precise
Imported using git-dsc-commit.
-rw-r--r--debian/changelog11
-rw-r--r--debian/cloud-init.preinst72
-rw-r--r--debian/patches/lp-1506187-azure_use_unique_vm_id.patch241
-rw-r--r--debian/patches/series1
4 files changed, 325 insertions, 0 deletions
diff --git a/debian/changelog b/debian/changelog
index 00a8dcf3..41438656 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,14 @@
+cloud-init (0.6.3-0ubuntu1.25) precise; urgency=medium
+
+ * Microsoft Azure: Use stable VM instance ID over SharedConfig.xml
+ (LP: #1506187)
+ - d/patches/lp-1506187-azure_use_unique_vm_id.patch: use DMI data for
+ the stable VM instance ID
+ - d/cloud-init.preinst: migrate existing instances to stable VM instance
+ ID on upgrade from prior versions of cloud-init.
+
+ -- Daniel Watkins <daniel.watkins@canonical.com> Mon, 25 Apr 2016 16:53:07 -0400
+
cloud-init (0.6.3-0ubuntu1.24) precise; urgency=medium
* d/patches/lp-1506244-azure-ssh-key-values.patch: AZURE: Add support
diff --git a/debian/cloud-init.preinst b/debian/cloud-init.preinst
index 42e84f7a..a6978882 100644
--- a/debian/cloud-init.preinst
+++ b/debian/cloud-init.preinst
@@ -105,6 +105,73 @@ convert_varlib_05x_06x() {
return 0
}
+azure_apply_new_instance_id_1506187() {
+ # With LP: #1506187, the Azure instance ID detection method was changed
+ # to use the DMI data. In order to prevent existing instances from thinking
+ # they are new instances, the instance ID needs to be updated here.
+
+ if grep DataSourceAzure /var/lib/cloud/instance/datasource > /dev/null 2>&1; then
+
+ product_id_f="/sys/devices/virtual/dmi/id/product_uuid"
+ instance_id_link="/var/lib/cloud/instance"
+
+ if [ ! -e "${product_id_f}" -o ! -e "${instance_id_link}" ]; then
+ return 0
+ fi
+
+ # Get the current instance ID's (new and old)
+ new_instance_id="$(cat ${product_id_f})"
+ old_instance_id="$(basename "$(readlink -f "${instance_id_link}")")"
+
+ if [ "${new_instance_id}" = "${old_instance_id}" ]; then
+ # this may have been applied for a prior version, i.e. upgrading
+ # from 14.04 to 16.04
+ return 0
+
+ elif [ -z "${new_instance_id}" -o -z "${old_instance_id}" ]; then
+ cat <<EOM
+
+WARNING: Failed to migrate old instance ID to new instance ID.
+ Cloud-init may detect this instance as a new instance upon reboot.
+ Please see: https://bugs.launchpad.net/bug/1506187
+
+EOM
+
+ elif [ "${new_instance_id}" != "${old_instance_id}" ]; then
+ cat <<EOM
+
+AZURE: this instance uses an unstable instance ID. Cloud-init will
+ migrate the instance ID from:
+ ${old_instance_id}
+ to:
+ ${new_instance_id}
+ For more information about this change, please see:
+ https://bugs.launchpad.net/bug/1506187
+ https://azure.microsoft.com/en-us/blog/accessing-and-using-azure-vm-unique-id
+
+EOM
+
+ # Write the new instance id
+ echo "${new_instance_id}" > /var/lib/cloud/data/instance-id
+
+ # Remove the symlink for the instance
+ rm /var/lib/cloud/instance
+
+ # Rename the old instance id to the new one
+ mv /var/lib/cloud/instances/${old_instance_id} \
+ /var/lib/cloud/instances/${new_instance_id}
+
+ # Link the old id to the new one, just incase
+ ln -s /var/lib/cloud/instances/${new_instance_id} \
+ /var/lib/cloud/instances/${old_instance_id}
+
+ # Make the active instance the new id
+ ln -s /var/lib/cloud/instances/${new_instance_id} \
+ /var/lib/cloud/instance
+ fi
+fi
+}
+
case "$1" in
install|upgrade)
# removing obsolete conffiles from the 'ec2-init' package
@@ -160,6 +227,11 @@ case "$1" in
ln -sf user-scripts /var/lib/cloud/instance/sem/config-scripts-user
fi
+ # 0.6.3-0ubuntu1.25 introduced new instance ID source for Azure
+ if dpkg --compare-versions "$2" le "0.6.3-0ubuntu1.25"; then
+ azure_apply_new_instance_id_1506187
+ fi
+
d=/etc/cloud/
if [ -f "$d/distro.cfg" ] && [ ! -f "$d/cloud.cfg.d/90_dpkg.cfg" ]; then
echo "moving $d/distro.cfg -> $d/cloud.cfg.d/90_dpkg.cfg"
diff --git a/debian/patches/lp-1506187-azure_use_unique_vm_id.patch b/debian/patches/lp-1506187-azure_use_unique_vm_id.patch
new file mode 100644
index 00000000..327a9fbe
--- /dev/null
+++ b/debian/patches/lp-1506187-azure_use_unique_vm_id.patch
@@ -0,0 +1,241 @@
+Author: Daniel Watkins <daniel.watkins@canonical.com>
+Origin: upstream
+Bug: https://launchpad.net/bugs/1506187
+Description: Handle new Azure instance IDs
+--- a/cloudinit/DataSourceAzure.py
++++ b/cloudinit/DataSourceAzure.py
+@@ -41,7 +41,6 @@
+ AGENT_START = ['service', 'walinuxagent', 'start']
+ BOUNCE_COMMAND = ['sh', '-xc',
+ "i=$interface; x=0; ifdown $i || x=$?; ifup $i || x=$?; exit $x"]
+-DATA_DIR_CLEAN_LIST = ['SharedConfig.xml']
+
+ BUILTIN_DS_CONFIG = {
+ 'agent_command': AGENT_START,
+@@ -139,21 +138,6 @@
+ mycfg = self.ds_cfg
+ ddir = mycfg['data_dir']
+
+- if found != ddir:
+- cached_ovfenv = util.load_file(
+- os.path.join(ddir, 'ovf-env.xml'), quiet=True)
+- if cached_ovfenv != files['ovf-env.xml']:
+- # source was not walinux-agent's datadir, so we have to clean
+- # up so 'wait_for_files' doesn't return early due to stale data
+- cleaned = []
+- for f in [os.path.join(ddir, f) for f in DATA_DIR_CLEAN_LIST]:
+- if os.path.exists(f):
+- futil.del_file(f)
+- cleaned.append(f)
+- if cleaned:
+- LOG.info("removed stale file(s) in '%s': %s",
+- ddir, str(cleaned))
+-
+ # walinux agent writes files world readable, but expects
+ # the directory to be protected.
+ write_files(ddir, files, dirmode=0700)
+@@ -185,9 +169,6 @@
+ LOG.warn("agent command '%s' failed.", mycfg['agent_command'])
+ util.logexc(LOG)
+
+- shcfgxml = os.path.join(mycfg['data_dir'], "SharedConfig.xml")
+- wait_for = [shcfgxml]
+-
+ fp_files = []
+ key_value = None
+ for pk in self.cfg.get('_pubkeys', []):
+@@ -200,24 +181,17 @@
+ LOG.debug("ssh authentication: using fingerprint from fabric")
+
+ start = time.time()
+- missing = wait_for_files(wait_for + fp_files)
++ missing = wait_for_files(fp_files)
+ if len(missing):
+ LOG.warn("Did not find files, but going on: %s", missing)
+ else:
+- LOG.debug("waited %.3f seconds for %d files to appear",
+- time.time() - start, len(wait_for))
+-
+- if shcfgxml in missing:
+- LOG.warn("SharedConfig.xml missing, using static instance-id")
+- else:
+- try:
+- self.metadata['instance-id'] = iid_from_shared_config(shcfgxml)
+- except ValueError as e:
+- LOG.warn("failed to get instance id in %s: %s" % (shcfgxml, e))
++ LOG.debug("waited %.3f seconds for files to appear",
++ time.time() - start)
+
+ pubkeys = key_value or pubkeys_from_crt_files(fp_files)
+
+ self.metadata['public-keys'] = pubkeys
++ self.metadata['instance-id'] = get_instance_id()
+
+ found_ephemeral = find_fabric_formatted_ephemeral_disk()
+ if found_ephemeral:
+@@ -239,6 +213,14 @@
+ def count_files(mp):
+ return len(fnmatch.filter(os.listdir(mp), '*[!cdrom]*'))
+
++
++def get_instance_id():
++ """
++ Read the instance ID from dmi data
++ """
++ return util.read_dmi_data('system-uuid')
++
++
+ def find_fabric_formatted_ephemeral_part():
+ """
+ Locate the first fabric formatted ephemeral device.
+@@ -683,25 +665,6 @@
+ return (md, ud, cfg, {'ovf-env.xml': contents})
+
+
+-def iid_from_shared_config(path):
+- with open(path, "rb") as fp:
+- content = fp.read()
+- return iid_from_shared_config_content(content)
+-
+-
+-def iid_from_shared_config_content(content):
+- """
+- find INSTANCE_ID in:
+- <?xml version="1.0" encoding="utf-8"?>
+- <SharedConfig version="1.0.0.0" goalStateIncarnation="1">
+- <Deployment name="INSTANCE_ID" guid="{...}" incarnation="0">
+- <Service name="..." guid="{00000000-0000-0000-0000-000000000000}" />
+- """
+- dom = minidom.parseString(content)
+- depnode = single_node_at_path(dom, ["SharedConfig", "Deployment"])
+- return depnode.attributes.get('name').value
+-
+-
+ class BrokenAzureDataSource(Exception):
+ pass
+
+--- a/cloudinit/util.py
++++ b/cloudinit/util.py
+@@ -46,6 +46,31 @@
+ except ImportError:
+ HAVE_LIBSELINUX = False
+
++# Path for DMI Data
++DMI_SYS_PATH = "/sys/class/dmi/id"
++
++# dmidecode and /sys/class/dmi/id/* use different names for the same value,
++# this allows us to refer to them by one canonical name
++DMIDECODE_TO_DMI_SYS_MAPPING = {
++ 'baseboard-asset-tag': 'board_asset_tag',
++ 'baseboard-manufacturer': 'board_vendor',
++ 'baseboard-product-name': 'board_name',
++ 'baseboard-serial-number': 'board_serial',
++ 'baseboard-version': 'board_version',
++ 'bios-release-date': 'bios_date',
++ 'bios-vendor': 'bios_vendor',
++ 'bios-version': 'bios_version',
++ 'chassis-asset-tag': 'chassis_asset_tag',
++ 'chassis-manufacturer': 'chassis_vendor',
++ 'chassis-serial-number': 'chassis_serial',
++ 'chassis-version': 'chassis_version',
++ 'system-manufacturer': 'sys_vendor',
++ 'system-product-name': 'product_name',
++ 'system-serial-number': 'product_serial',
++ 'system-uuid': 'product_uuid',
++ 'system-version': 'product_version',
++}
++
+ _DNS_REDIRECT_IP = None
+ LOG = logging.getLogger("cloudinit")
+
+@@ -982,3 +1007,90 @@
+
+ return os.path.isfile("/sys/class/block/%s/partition" % device)
+
++
++def _read_dmi_syspath(key):
++ """
++ Reads dmi data with from /sys/class/dmi/id
++ """
++ if key not in DMIDECODE_TO_DMI_SYS_MAPPING:
++ return None
++ mapped_key = DMIDECODE_TO_DMI_SYS_MAPPING[key]
++ dmi_key_path = "{0}/{1}".format(DMI_SYS_PATH, mapped_key)
++ LOG.debug("querying dmi data %s", dmi_key_path)
++ try:
++ if not os.path.exists(dmi_key_path):
++ LOG.debug("did not find %s", dmi_key_path)
++ return None
++
++ key_data = load_file(dmi_key_path)
++ if not key_data:
++ LOG.debug("%s did not return any data", dmi_key_path)
++ return None
++
++ LOG.debug("dmi data %s returned %s", dmi_key_path, key_data)
++ return key_data.strip()
++
++ except Exception:
++ LOG.warn("failed read of %s", dmi_key_path)
++ logexc(LOG)
++ return None
++
++
++def _call_dmidecode(key, dmidecode_path):
++ """
++ Calls out to dmidecode to get the data out. This is mostly for supporting
++ OS's without /sys/class/dmi/id support.
++ """
++ try:
++ cmd = [dmidecode_path, "--string", key]
++ (result, _err) = subp(cmd)
++ LOG.debug("dmidecode returned '%s' for '%s'", result, key)
++ return result
++ except (IOError, OSError) as _err:
++ LOG.debug('failed dmidecode cmd: %s\n%s', cmd, _err.message)
++ return None
++
++
++def which(program):
++ # Return path of program for execution if found in path
++ def is_exe(fpath):
++ return os.path.isfile(fpath) and os.access(fpath, os.X_OK)
++
++ _fpath, _ = os.path.split(program)
++ if _fpath:
++ if is_exe(program):
++ return program
++ else:
++ for path in os.environ.get("PATH", "").split(os.pathsep):
++ path = path.strip('"')
++ exe_file = os.path.join(path, program)
++ if is_exe(exe_file):
++ return exe_file
++
++ return None
++
++
++def read_dmi_data(key):
++ """
++ Wrapper for reading DMI data.
++
++ This will do the following (returning the first that produces a
++ result):
++ 1) Use a mapping to translate `key` from dmidecode naming to
++ sysfs naming and look in /sys/class/dmi/... for a value.
++ 2) Use `key` as a sysfs key directly and look in /sys/class/dmi/...
++ 3) Fall-back to passing `key` to `dmidecode --string`.
++
++ If all of the above fail to find a value, None will be returned.
++ """
++ syspath_value = _read_dmi_syspath(key)
++ if syspath_value is not None:
++ return syspath_value
++
++ dmidecode_path = which('dmidecode')
++ if dmidecode_path:
++ return _call_dmidecode(key, dmidecode_path)
++
++ LOG.warn("did not find either path %s or dmidecode command",
++ DMI_SYS_PATH)
++ return None
diff --git a/debian/patches/series b/debian/patches/series
index d1b9275c..0132a1f0 100644
--- a/debian/patches/series
+++ b/debian/patches/series
@@ -40,3 +40,4 @@ lp-1458052-redact-azure-password.patch
lp-1382481-cloudstack-vr.patch
lp-1177432-same-archives-as-ubuntu-server.patch
lp-1506244-azure-ssh-key-values.patch
+lp-1506187-azure_use_unique_vm_id.patch