cloud-init-dev team mailing list archive
-
cloud-init-dev team
-
Mailing list archive
-
Message #00521
[Merge] lp:~jfontan/cloud-init/opennebula-4.8 into lp:cloud-init
Javi Fontan has proposed merging lp:~jfontan/cloud-init/opennebula-4.8 into lp:cloud-init.
Requested reviews:
cloud init development team (cloud-init-dev)
For more details, see:
https://code.launchpad.net/~jfontan/cloud-init/opennebula-4.8/+merge/234207
Changes to bring new functionality to the OpenNebula DataSource (ONE 4.8) and fix problems with the new interface naming scheme. It also addresses some problems with RHEL/CentOS 7 that made the old version incompatible.
There is a change that I'm not sure you are happy with it. I've added a ifdown command before ifup as in Ubuntu the interface did not get configured as the interface was already up. If there is a better way to fix this I will be happy to change the code.
--
https://code.launchpad.net/~jfontan/cloud-init/opennebula-4.8/+merge/234207
Your team cloud init development team is requested to review the proposed merge of lp:~jfontan/cloud-init/opennebula-4.8 into lp:cloud-init.
=== modified file 'cloudinit/distros/__init__.py'
--- cloudinit/distros/__init__.py 2014-09-10 18:32:37 +0000
+++ cloudinit/distros/__init__.py 2014-09-10 21:01:03 +0000
@@ -271,13 +271,19 @@
util.write_file(self.hosts_fn, contents.getvalue(), mode=0644)
def _bring_up_interface(self, device_name):
- cmd = ['ifup', device_name]
- LOG.debug("Attempting to run bring up interface %s using command %s",
- device_name, cmd)
+ down_cmd = ['ifdown', device_name]
+ up_cmd = ['ifup', device_name]
+ LOG.debug("Attempting to run bring up interface %s using commands %s, %s",
+ device_name, down_cmd, up_cmd)
try:
- (_out, err) = util.subp(cmd)
- if len(err):
- LOG.warn("Running %s resulted in stderr output: %s", cmd, err)
+ (_out, err) = util.subp(down_cmd)
+ if len(err):
+ LOG.warn("Running %s resulted in stderr output: %s", down_cmd, err)
+
+ (_out, err) = util.subp(up_cmd)
+ if len(err):
+ LOG.warn("Running %s resulted in stderr output: %s", up_cmd, err)
+
return True
except util.ProcessExecutionError:
util.logexc(LOG, "Running interface command %s failed", cmd)
@@ -525,6 +531,13 @@
util.subp(['usermod', '-a', '-G', name, member])
LOG.info("Added user '%s' to group '%s'" % (member, name))
+ # Command used to execute command with an specific user
+ def switch_user_cmd(self, user):
+ return ['sudo', '-u', user]
+
+ def disable_network_manager(self):
+ pass
+
def _get_package_mirror_info(mirror_info, availability_zone=None,
mirror_filter=util.search_for_mirror):
=== modified file 'cloudinit/distros/rhel.py'
--- cloudinit/distros/rhel.py 2014-02-03 22:03:14 +0000
+++ cloudinit/distros/rhel.py 2014-09-10 21:01:03 +0000
@@ -208,3 +208,15 @@
def update_package_sources(self):
self._runner.run("update-sources", self.package_command,
["makecache"], freq=PER_INSTANCE)
+
+ def switch_user_cmd(self, user):
+ # In RHEL/CentOS sudo has requiretty defined and sudo can not be used
+ return ['runuser', '-u', user, '--']
+
+ def disable_network_manager(self):
+ # ifup fails if NetworkManager is running
+ 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")
\ No newline at end of file
=== modified file 'cloudinit/sources/DataSourceOpenNebula.py'
--- cloudinit/sources/DataSourceOpenNebula.py 2014-08-26 19:53:41 +0000
+++ cloudinit/sources/DataSourceOpenNebula.py 2014-09-10 21:01:03 +0000
@@ -42,7 +42,6 @@
CONTEXT_DISK_FILES = ["context.sh"]
VALID_DSMODES = ("local", "net", "disabled")
-
class DataSourceOpenNebula(sources.DataSource):
def __init__(self, sys_cfg, distro, paths):
sources.DataSource.__init__(self, sys_cfg, distro, paths)
@@ -69,10 +68,12 @@
for cdev in candidates:
try:
if os.path.isdir(self.seed_dir):
- results = read_context_disk_dir(cdev, asuser=parseuser)
+ results = read_context_disk_dir(cdev, { 'distro': self.distro,
+ 'asuser': parseuser })
elif cdev.startswith("/dev"):
results = util.mount_cb(cdev, read_context_disk_dir,
- data=parseuser)
+ data={ 'distro': self.distro,
+ 'asuser': parseuser })
except NonContextDiskDir:
continue
except BrokenContextDiskDir as exc:
@@ -149,63 +150,99 @@
class OpenNebulaNetwork(object):
REG_DEV_MAC = re.compile(
- r'^\d+: (eth\d+):.*?link\/ether (..:..:..:..:..:..) ?',
+ r'^\d+: (\w+):.*?link\/\w+ (..:..:..:..:..:..) ?',
re.MULTILINE | re.DOTALL)
- def __init__(self, ip, context):
+ def __init__(self, ip, context, distro):
self.ip = ip
self.context = context
self.ifaces = self.get_ifaces()
+ self.distro = distro
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_iface_var(self, dev, var):
+ var_name = dev.upper() + '_' + var
+ if var_name in self.context:
+ return self.context[var_name]
+ else:
+ return None
+
def get_ip(self, dev, components):
- var_name = dev.upper() + '_IP'
- if var_name in self.context:
- return self.context[var_name]
+ var = self.get_iface_var(dev, 'IP')
+ if var:
+ return var
else:
return '.'.join(components)
def get_mask(self, dev):
- var_name = dev.upper() + '_MASK'
- if var_name in self.context:
- return self.context[var_name]
+ var = self.get_iface_var(dev, 'MASK')
+ if var:
+ return var
else:
return '255.255.255.0'
def get_network(self, dev, components):
- var_name = dev.upper() + '_NETWORK'
- if var_name in self.context:
- return self.context[var_name]
+ var = self.get_iface_var(dev, 'NETWORK')
+ if var:
+ return var
else:
return '.'.join(components[:-1]) + '.0'
+ def is_gateway_iface(self, dev):
+ if 'GATEWAY_IFACE' in self.context:
+ val=self.context['GATEWAY_IFACE']
+ dev_num=val.replace('ETH', '')
+ return ('ETH'+dev_num) == dev
+ else:
+ return True
+
def get_gateway(self, dev):
- var_name = dev.upper() + '_GATEWAY'
- if var_name in self.context:
- return self.context[var_name]
+ if self.is_gateway_iface(dev):
+ return self.get_iface_var(dev, 'GATEWAY')
else:
return None
def get_dns(self, dev):
- var_name = dev.upper() + '_DNS'
- if var_name in self.context:
- return self.context[var_name]
- else:
- return None
+ return self.get_iface_var(dev, 'DNS')
def get_domain(self, dev):
- var_name = dev.upper() + '_DOMAIN'
- if var_name in self.context:
- return self.context[var_name]
+ var = self.get_iface_var(dev, 'SEARCH_DOMAIN')
+ if var:
+ return var
+
+ return self.get_iface_var(dev, 'DOMAIN')
+
+ def get_force_ipv4(self, dev):
+ return self.get_iface_var(dev, 'CONTEXT_FORCE_IPV4')
+
+ def get_ip6(self, dev):
+ return self.get_iface_var(dev, 'IP6')
+
+ def get_gateway6(self, dev):
+ if self.is_gateway_iface(dev):
+ return self.get_iface_var(dev, 'GATEWAY6')
else:
return None
+ 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 gen_conf(self):
global_dns = []
if 'DNS' in self.context:
@@ -216,34 +253,65 @@
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) and self.distro:
+ self.distro.disable_network_manager()
+
+ for interface in context_interfaces:
+ mac = self.context[interface+"_MAC"]
+ dev = self.ifaces[mac]
+
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))
-
- gateway = self.get_gateway(dev)
- if gateway:
- conf.append(' gateway ' + gateway)
-
- domain = self.get_domain(dev)
- if domain:
- conf.append(' dns-search ' + domain)
-
- # add global DNS servers to all interfaces
- dns = self.get_dns(dev)
+ all_dns = None
+ dns = self.get_dns(interface)
if global_dns or dns:
all_dns = global_dns
if dns:
all_dns.append(dns)
- conf.append(' dns-nameservers ' + ' '.join(all_dns))
-
- conf.append('')
+
+ ip6 = self.get_ip6(dev)
+ conf.append('auto ' + dev)
+
+ if self.get_force_ipv4(dev) or not ip6:
+ conf.append('iface ' + dev + ' inet static')
+ conf.append(' address ' + self.get_ip(interface, ip_components))
+ conf.append(' network ' + self.get_network(interface, ip_components))
+ conf.append(' netmask ' + self.get_mask(interface))
+
+ gateway = self.get_gateway(interface)
+ if gateway:
+ conf.append(' gateway ' + gateway)
+
+ domain = self.get_domain(interface)
+ if domain:
+ conf.append(' dns-search ' + domain)
+
+ # add global DNS servers to all interfaces
+ if all_dns:
+ conf.append(' dns-nameservers ' + ' '.join(all_dns))
+
+ conf.append('')
+
+ if ip6:
+ conf.append('iface ' + dev + ' inet6 static')
+ conf.append(' address ' + ip6)
+ conf.append(' netmask 64')
+
+ gateway = self.get_gateway6(dev)
+ if gateway:
+ conf.append(' gateway ' + gateway)
+
+ domain = self.get_domain(interface)
+ if domain:
+ conf.append(' dns-search ' + domain)
+
+ # add global DNS servers to all interfaces
+ if all_dns:
+ conf.append(' dns-nameservers ' + ' '.join(all_dns))
+
+ conf.append('')
return "\n".join(conf)
@@ -353,12 +421,21 @@
return ret
-def read_context_disk_dir(source_dir, asuser=None):
+def read_context_disk_dir(source_dir, options={}):
"""
read_context_disk_dir(source_dir):
read source_dir and return a tuple with metadata dict and user-data
string populated. If not a valid dir, raise a NonContextDiskDir
"""
+
+ asuser = None
+ if options.has_key('asuser'):
+ asuser = options['asuser']
+
+ distro = None
+ if options.has_key('distro'):
+ distro = options['distro']
+
found = {}
for af in CONTEXT_DISK_FILES:
fn = os.path.join(source_dir, af)
@@ -382,7 +459,14 @@
with open(os.path.join(source_dir, 'context.sh'), 'r') as f:
content = f.read().strip()
- context = parse_shell_config(content, asuser=asuser)
+ if distro:
+ switch_cmd = distro.switch_user_cmd
+ else:
+ switch_cmd = None
+
+ context = parse_shell_config(content,
+ asuser=asuser,
+ switch_user_cb=switch_cmd)
except util.ProcessExecutionError as e:
raise BrokenContextDiskDir("Error processing context.sh: %s" % (e))
except IOError as e:
@@ -409,7 +493,7 @@
# custom hostname -- try hostname or leave cloud-init
# itself create hostname from IP address later
- for k in ('HOSTNAME', 'PUBLIC_IP', 'IP_PUBLIC', 'ETH0_IP'):
+ for k in ('SET_HOSTNAME', 'HOSTNAME', 'PUBLIC_IP', 'IP_PUBLIC', 'ETH0_IP'):
if k in context:
results['metadata']['local-hostname'] = context[k]
break
@@ -436,7 +520,7 @@
for k in context.keys():
if re.match(r'^ETH\d+_IP$', k):
(out, _) = util.subp(['/sbin/ip', 'link'])
- net = OpenNebulaNetwork(out, context)
+ net = OpenNebulaNetwork(out, context, distro)
results['network-interfaces'] = net.gen_conf()
break
@@ -453,3 +537,4 @@
# Return a list of data sources that match this set of dependencies
def get_datasource_list(depends):
return sources.list_from_depends(depends, datasources)
+
=== modified file 'tests/unittests/test_datasource/test_opennebula.py'
--- tests/unittests/test_datasource/test_opennebula.py 2014-07-23 16:50:45 +0000
+++ tests/unittests/test_datasource/test_opennebula.py 2014-09-10 21:01:03 +0000
@@ -234,29 +234,17 @@
super(TestOpenNebulaNetwork, self).setUp()
def test_lo(self):
- net = ds.OpenNebulaNetwork('', {})
- self.assertEqual(net.gen_conf(), u'''\
-auto lo
-iface lo inet loopback
-''')
-
- def test_eth0(self):
- net = ds.OpenNebulaNetwork(CMD_IP_OUT, {})
- self.assertEqual(net.gen_conf(), u'''\
-auto lo
-iface lo inet loopback
-
-auto eth0
-iface eth0 inet static
- address 10.18.1.1
- network 10.18.1.0
- netmask 255.255.255.0
+ net = ds.OpenNebulaNetwork('', {}, None)
+ self.assertEqual(net.gen_conf(), u'''\
+auto lo
+iface lo inet loopback
''')
def test_eth0_override(self):
context = {
'DNS': '1.2.3.8',
'ETH0_IP': '1.2.3.4',
+ 'ETH0_MAC': '02:00:0a:12:01:01',
'ETH0_NETWORK': '1.2.3.0',
'ETH0_MASK': '255.255.0.0',
'ETH0_GATEWAY': '1.2.3.5',
@@ -264,21 +252,103 @@
'ETH0_DNS': '1.2.3.6 1.2.3.7'
}
- net = ds.OpenNebulaNetwork(CMD_IP_OUT, context)
- self.assertEqual(net.gen_conf(), u'''\
-auto lo
-iface lo inet loopback
-
-auto eth0
-iface eth0 inet static
- address 1.2.3.4
- network 1.2.3.0
- netmask 255.255.0.0
- gateway 1.2.3.5
- dns-search example.com
- dns-nameservers 1.2.3.8 1.2.3.6 1.2.3.7
-''')
-
+ net = ds.OpenNebulaNetwork(CMD_IP_OUT, context, None)
+ self.assertEqual(net.gen_conf(), u'''\
+auto lo
+iface lo inet loopback
+
+auto eth0
+iface eth0 inet static
+ address 1.2.3.4
+ network 1.2.3.0
+ netmask 255.255.0.0
+ gateway 1.2.3.5
+ dns-search example.com
+ dns-nameservers 1.2.3.8 1.2.3.6 1.2.3.7
+''')
+
+ def test_eth0_override_with_gateway(self):
+ context = {
+ 'DNS': '1.2.3.8',
+ 'ETH0_IP': '1.2.3.4',
+ 'ETH0_MAC': '02:00:0a:12:01:01',
+ 'ETH0_NETWORK': '1.2.3.0',
+ 'ETH0_MASK': '255.255.0.0',
+ 'ETH0_GATEWAY': '1.2.3.5',
+ 'ETH0_SEARCH_DOMAIN': 'example.com',
+ 'ETH0_DNS': '1.2.3.6 1.2.3.7',
+ 'GATEWAY_IFACE': 'ETH0'
+ }
+
+ net = ds.OpenNebulaNetwork(CMD_IP_OUT, context, None)
+ self.assertEqual(net.gen_conf(), u'''\
+auto lo
+iface lo inet loopback
+
+auto eth0
+iface eth0 inet static
+ address 1.2.3.4
+ network 1.2.3.0
+ netmask 255.255.0.0
+ gateway 1.2.3.5
+ dns-search example.com
+ dns-nameservers 1.2.3.8 1.2.3.6 1.2.3.7
+''')
+
+ def test_eth0_override_without_gateway(self):
+ context = {
+ 'DNS': '1.2.3.8',
+ 'ETH0_IP': '1.2.3.4',
+ 'ETH0_MAC': '02:00:0a:12:01:01',
+ 'ETH0_NETWORK': '1.2.3.0',
+ 'ETH0_MASK': '255.255.0.0',
+ 'ETH0_GATEWAY': '1.2.3.5',
+ 'ETH0_DOMAIN': 'example.com',
+ 'ETH0_DNS': '1.2.3.6 1.2.3.7',
+ 'GATEWAY_IFACE': '1'
+ }
+
+ net = ds.OpenNebulaNetwork(CMD_IP_OUT, context, None)
+ self.assertEqual(net.gen_conf(), u'''\
+auto lo
+iface lo inet loopback
+
+auto eth0
+iface eth0 inet static
+ address 1.2.3.4
+ network 1.2.3.0
+ netmask 255.255.0.0
+ dns-search example.com
+ dns-nameservers 1.2.3.8 1.2.3.6 1.2.3.7
+''')
+
+ def test_eth0_override_with_ipv6(self):
+ context = {
+ 'DNS': '1.2.3.8',
+ 'ETH0_IP': '1.2.3.4',
+ 'ETH0_MAC': '02:00:0a:12:01:01',
+ 'ETH0_NETWORK': '1.2.3.0',
+ 'ETH0_MASK': '255.255.0.0',
+ 'ETH0_GATEWAY': '1.2.3.5',
+ 'ETH0_DOMAIN': 'example.com',
+ 'ETH0_DNS': '1.2.3.6 1.2.3.7',
+ 'ETH0_IP6': '2001:470:801f::1',
+ 'ETH0_GATEWAY6': '2001:470:1f05:15a::1'
+ }
+
+ net = ds.OpenNebulaNetwork(CMD_IP_OUT, context, None)
+ self.assertEqual(net.gen_conf(), u'''\
+auto lo
+iface lo inet loopback
+
+auto eth0
+iface eth0 inet6 static
+ address 2001:470:801f::1
+ netmask 64
+ gateway 2001:470:1f05:15a::1
+ dns-search example.com
+ dns-nameservers 1.2.3.8 1.2.3.6 1.2.3.7
+''')
class TestParseShellConfig(MockerTestCase):
def test_no_seconds(self):
Follow ups