summaryrefslogtreecommitdiff
path: root/cloudinit
diff options
context:
space:
mode:
authorSankar Tanguturi <stanguturi@stanguturi-rhel>2016-02-09 17:54:07 -0800
committerSankar Tanguturi <stanguturi@stanguturi-rhel>2016-02-09 17:54:07 -0800
commit39f668e5db8d09c46eee3a5df73a69f8d85ba489 (patch)
tree98812b43a9fa3d4b712f56ab2ddd4a9068361398 /cloudinit
parent9ba841efc4263fcd1ec8eb266a3afa83b525ba49 (diff)
downloadvyos-cloud-init-39f668e5db8d09c46eee3a5df73a69f8d85ba489.tar.gz
vyos-cloud-init-39f668e5db8d09c46eee3a5df73a69f8d85ba489.zip
- Added the code to configure the NICs.
- Added the code to detect VMware Virtual Platform and apply the customization based on the 'Customization Specification File' Pushed into the guest VM.
Diffstat (limited to 'cloudinit')
-rw-r--r--cloudinit/sources/DataSourceOVF.py107
-rw-r--r--cloudinit/sources/helpers/vmware/imc/config_nic.py246
-rw-r--r--cloudinit/sources/helpers/vmware/imc/nic.py28
3 files changed, 372 insertions, 9 deletions
diff --git a/cloudinit/sources/DataSourceOVF.py b/cloudinit/sources/DataSourceOVF.py
index 58a4b2a2..add7d243 100644
--- a/cloudinit/sources/DataSourceOVF.py
+++ b/cloudinit/sources/DataSourceOVF.py
@@ -24,11 +24,16 @@ from xml.dom import minidom
import base64
import os
+import shutil
import re
+import time
from cloudinit import log as logging
from cloudinit import sources
from cloudinit import util
+from cloudinit.sources.helpers.vmware.imc.config import Config
+from cloudinit.sources.helpers.vmware.imc.config_file import ConfigFile
+from cloudinit.sources.helpers.vmware.imc.config_nic import NicConfigurator
LOG = logging.getLogger(__name__)
@@ -50,13 +55,51 @@ class DataSourceOVF(sources.DataSource):
found = []
md = {}
ud = ""
+ vmwarePlatformFound = False
+ vmwareImcConfigFilePath = ''
defaults = {
"instance-id": "iid-dsovf",
}
(seedfile, contents) = get_ovf_env(self.paths.seed_dir)
- if seedfile:
+ dmi_info = dmi_data()
+ system_uuid = ""
+ system_type = ""
+
+ if dmi_info is False:
+ LOG.debug("No dmidata utility found")
+ else:
+ system_uuid, system_type = tuple(dmi_info)
+
+ if 'vmware' in system_type.lower():
+ LOG.debug("VMware Virtual Platform found")
+ deployPkgPluginPath = search_file("/usr/lib/vmware-tools", "libdeployPkgPlugin.so")
+ if deployPkgPluginPath:
+ vmwareImcConfigFilePath = util.log_time(logfunc=LOG.debug,
+ msg="waiting for configuration file",
+ func=wait_for_imc_cfg_file,
+ args=("/tmp", "cust.cfg"))
+
+ if vmwareImcConfigFilePath:
+ LOG.debug("Found VMware DeployPkg Config File Path at %s" % vmwareImcConfigFilePath)
+ else:
+ LOG.debug("Didn't find VMware DeployPkg Config File Path")
+
+ if vmwareImcConfigFilePath:
+ try:
+ cf = ConfigFile(vmwareImcConfigFilePath)
+ conf = Config(cf)
+ (md, ud, cfg) = read_vmware_imc(conf)
+ nicConfigurator = NicConfigurator(conf.nics)
+ nicConfigurator.configure()
+ vmwarePlatformFound = True
+ except Exception as inst:
+ LOG.debug("Error while parsing the Customization Config File")
+ finally:
+ dirPath = os.path.dirname(vmwareImcConfigFilePath)
+ shutil.rmtree(dirPath)
+ elif seedfile:
# Found a seed dir
seed = os.path.join(self.paths.seed_dir, seedfile)
(md, ud, cfg) = read_ovf_environment(contents)
@@ -76,7 +119,7 @@ class DataSourceOVF(sources.DataSource):
found.append(name)
# There was no OVF transports found
- if len(found) == 0:
+ if len(found) == 0 and not vmwarePlatformFound:
return False
if 'seedfrom' in md and md['seedfrom']:
@@ -108,7 +151,7 @@ class DataSourceOVF(sources.DataSource):
def get_public_ssh_keys(self):
if 'public-keys' not in self.metadata:
- return []
+ return []
pks = self.metadata['public-keys']
if isinstance(pks, (list)):
return pks
@@ -129,6 +172,31 @@ class DataSourceOVFNet(DataSourceOVF):
self.supported_seed_starts = ("http://", "https://", "ftp://")
+def wait_for_imc_cfg_file(directoryPath, filename, maxwait=180, naplen=5):
+ waited = 0
+
+ while waited < maxwait:
+ fileFullPath = search_file(directoryPath, filename)
+ if fileFullPath:
+ return fileFullPath
+ time.sleep(naplen)
+ waited += naplen
+ return None
+
+# This will return a dict with some content
+# meta-data, user-data, some config
+def read_vmware_imc(config):
+ md = {}
+ cfg = {}
+ ud = ""
+ if config.host_name:
+ if config.domain_name:
+ md['local-hostname'] = config.host_name + "." + config.domain_name
+ else:
+ md['local-hostname'] = config.host_name
+
+ return (md, ud, cfg)
+
# This will return a dict with some content
# meta-data, user-data, some config
def read_ovf_environment(contents):
@@ -280,6 +348,39 @@ def get_properties(contents):
return props
+def dmi_data():
+ sys_uuid = util.read_dmi_data("system-uuid")
+ sys_type = util.read_dmi_data("system-product-name")
+
+ if not sys_uuid or not sys_type:
+ return None
+
+ return (sys_uuid.lower(), sys_type)
+
+def search_file(directoryPath, filename):
+ if not directoryPath or not filename:
+ return None
+
+ dirs = []
+
+ if os.path.isdir(directoryPath):
+ dirs.append(directoryPath)
+
+ while dirs:
+ dir = dirs.pop()
+ children = []
+ try:
+ children.extend(os.listdir(dir))
+ except:
+ LOG.debug("Ignoring the error while searching the directory %s" % dir)
+ for child in children:
+ childFullPath = os.path.join(dir, child)
+ if os.path.isdir(childFullPath):
+ dirs.append(childFullPath)
+ elif child == filename:
+ return childFullPath
+
+ return None
class XmlError(Exception):
pass
diff --git a/cloudinit/sources/helpers/vmware/imc/config_nic.py b/cloudinit/sources/helpers/vmware/imc/config_nic.py
new file mode 100644
index 00000000..8e2fc5d3
--- /dev/null
+++ b/cloudinit/sources/helpers/vmware/imc/config_nic.py
@@ -0,0 +1,246 @@
+# vi: ts=4 expandtab
+#
+# Copyright (C) 2015 Canonical Ltd.
+# Copyright (C) 2016 VMware INC.
+#
+# Author: Sankar Tanguturi <stanguturi@vmware.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 logging
+import os
+import subprocess
+import re
+
+logger = logging.getLogger(__name__)
+
+
+class NicConfigurator:
+ def __init__(self, nics):
+ """
+ Initialize the Nic Configurator
+ @param nics (list) an array of nics to configure
+ """
+ self.nics = nics
+ self.mac2Name = {}
+ self.ipv4PrimaryGateway = None
+ self.ipv6PrimaryGateway = None
+ self.find_devices()
+ self._primaryNic = self.get_primary_nic()
+
+ def get_primary_nic(self):
+ """
+ Retrieve the primary nic if it exists
+ @return (NicBase): the primary nic if exists, None otherwise
+ """
+ primaryNic = None
+
+ for nic in self.nics:
+ if nic.primary:
+ if primaryNic:
+ raise Exception('There can only be one primary nic',
+ primaryNic.mac, nic.mac)
+ primaryNic = nic
+
+ return primaryNic
+
+ def find_devices(self):
+ """
+ Create the mac2Name dictionary
+ The mac address(es) are in the lower case
+ """
+ cmd = 'ip addr show'
+ outText = subprocess.check_output(cmd, shell=True).decode()
+ sections = re.split(r'\n\d+: ', '\n' + outText)[1:]
+
+ macPat = r'link/ether (([0-9A-Fa-f]{2}[:]){5}([0-9A-Fa-f]{2}))'
+ for section in sections:
+ matcher = re.search(macPat, section)
+ if not matcher: # Only keep info about nics
+ continue
+ mac = matcher.group(1).lower()
+ name = section.split(':', 1)[0]
+ self.mac2Name[mac] = name
+
+ def gen_one_nic(self, nic):
+ """
+ Return the lines needed to configure a nic
+ @return (str list): the string list to configure the nic
+ @param nic (NicBase): the nic to configure
+ """
+ lines = []
+ name = self.mac2Name.get(nic.mac.lower())
+ if not name:
+ raise ValueError('No known device has MACADDR: %s' % nic.mac)
+
+ if nic.onboot:
+ lines.append('auto %s' % name)
+
+ # Customize IPv4
+ lines.extend(self.gen_ipv4(name, nic))
+
+ # Customize IPv6
+ lines.extend(self.gen_ipv6(name, nic))
+
+ lines.append('')
+
+ return lines
+
+ def gen_ipv4(self, name, nic):
+ """
+ Return the lines needed to configure the IPv4 setting of a nic
+ @return (str list): the string list to configure the gateways
+ @param name (str): name of the nic
+ @param nic (NicBase): the nic to configure
+ """
+ lines = []
+
+ bootproto = nic.bootProto.lower()
+ if nic.ipv4_mode.lower() == 'disabled':
+ bootproto = 'manual'
+ lines.append('iface %s inet %s' % (name, bootproto))
+
+ if bootproto != 'static':
+ return lines
+
+ # Static Ipv4
+ v4 = nic.staticIpv4
+ if v4.ip:
+ lines.append(' address %s' % v4.ip)
+ if v4.netmask:
+ lines.append(' netmask %s' % v4.netmask)
+
+ # Add the primary gateway
+ if nic.primary and v4.gateways:
+ self.ipv4PrimaryGateway = v4.gateways[0]
+ lines.append(' gateway %s metric 0' % self.ipv4PrimaryGateway)
+ return lines
+
+ # Add routes if there is no primary nic
+ if not self._primaryNic:
+ lines.extend(self.gen_ipv4_route(nic, v4.gateways))
+
+ return lines
+
+ def gen_ipv4_route(self, nic, gateways):
+ """
+ Return the lines needed to configure additional Ipv4 route
+ @return (str list): the string list to configure the gateways
+ @param nic (NicBase): the nic to configure
+ @param gateways (str list): the list of gateways
+ """
+ lines = []
+
+ for gateway in gateways:
+ lines.append(' up route add default gw %s metric 10000' % gateway)
+
+ return lines
+
+ def gen_ipv6(self, name, nic):
+ """
+ Return the lines needed to configure the gateways for a nic
+ @return (str list): the string list to configure the gateways
+ @param name (str): name of the nic
+ @param nic (NicBase): the nic to configure
+ """
+ lines = []
+
+ if not nic.staticIpv6:
+ return lines
+
+ # Static Ipv6
+ addrs = nic.staticIpv6
+ lines.append('iface %s inet6 static' % name)
+ lines.append(' address %s' % addrs[0].ip)
+ lines.append(' netmask %s' % addrs[0].netmask)
+
+ for addr in addrs[1:]:
+ lines.append(' up ifconfig %s inet6 add %s/%s' % (name, addr.ip,
+ addr.netmask))
+ # Add the primary gateway
+ if nic.primary:
+ for addr in addrs:
+ if addr.gateway:
+ self.ipv6PrimaryGateway = addr.gateway
+ lines.append(' gateway %s' % self.ipv6PrimaryGateway)
+ return lines
+
+ # Add routes if there is no primary nic
+ if not self._primaryNic:
+ lines.extend(self._genIpv6Route(name, nic, addrs))
+
+ return lines
+
+ def _genIpv6Route(self, name, nic, addrs):
+ lines = []
+
+ for addr in addrs:
+ lines.append(' up route -A inet6 add default gw %s metric 10000' %
+ addr.gateway)
+
+ return lines
+
+ def generate(self):
+ """Return the lines that is needed to configure the nics"""
+ lines = []
+ lines.append('iface lo inet loopback')
+ lines.append('auto lo')
+ lines.append('')
+
+ for nic in self.nics:
+ lines.extend(self.gen_one_nic(nic))
+
+ return lines
+
+ def clear_dhcp(self):
+ logger.info('Clearing DHCP leases')
+
+ subprocess.call('pkill dhclient', shell=True)
+ subprocess.check_call('rm -f /var/lib/dhcp/*', shell=True)
+
+ def if_down_up(self):
+ names = []
+ for nic in self.nics:
+ name = self.mac2Name.get(nic.mac.lower())
+ names.append(name)
+
+ for name in names:
+ logger.info('Bring down interface %s' % name)
+ subprocess.check_call('ifdown %s' % name, shell=True)
+
+ self.clear_dhcp()
+
+ for name in names:
+ logger.info('Bring up interface %s' % name)
+ subprocess.check_call('ifup %s' % name, shell=True)
+
+ def configure(self):
+ """
+ Configure the /etc/network/intefaces
+ Make a back up of the original
+ """
+ containingDir = '/etc/network'
+
+ interfaceFile = os.path.join(containingDir, 'interfaces')
+ originalFile = os.path.join(containingDir,
+ 'interfaces.before_vmware_customization')
+
+ if not os.path.exists(originalFile) and os.path.exists(interfaceFile):
+ os.rename(interfaceFile, originalFile)
+
+ lines = self.generate()
+ with open(interfaceFile, 'w') as fp:
+ for line in lines:
+ fp.write('%s\n' % line)
+
+ self.if_down_up()
diff --git a/cloudinit/sources/helpers/vmware/imc/nic.py b/cloudinit/sources/helpers/vmware/imc/nic.py
index a7594874..6628a3ec 100644
--- a/cloudinit/sources/helpers/vmware/imc/nic.py
+++ b/cloudinit/sources/helpers/vmware/imc/nic.py
@@ -47,21 +47,37 @@ class Nic(NicBase):
@property
def primary(self):
- value = self._get('PRIMARY').lower()
- return value == 'yes' or value == 'true'
+ value = self._get('PRIMARY')
+ if value:
+ value = value.lower()
+ return value == 'yes' or value == 'true'
+ else:
+ return False
@property
def onboot(self):
- value = self._get('ONBOOT').lower()
- return value == 'yes' or value == 'true'
+ value = self._get('ONBOOT')
+ if value:
+ value = value.lower()
+ return value == 'yes' or value == 'true'
+ else:
+ return False
@property
def bootProto(self):
- return self._get('BOOTPROTO').lower()
+ value = self._get('BOOTPROTO')
+ if value:
+ return value.lower()
+ else:
+ return ""
@property
def ipv4_mode(self):
- return self._get('IPv4_MODE').lower()
+ value = self._get('IPv4_MODE')
+ if value:
+ return value.lower()
+ else:
+ return ""
@property
def staticIpv4(self):