summaryrefslogtreecommitdiff
path: root/cloudinit/net/sysconfig.py
diff options
context:
space:
mode:
Diffstat (limited to 'cloudinit/net/sysconfig.py')
-rw-r--r--cloudinit/net/sysconfig.py400
1 files changed, 0 insertions, 400 deletions
diff --git a/cloudinit/net/sysconfig.py b/cloudinit/net/sysconfig.py
deleted file mode 100644
index c53acf71..00000000
--- a/cloudinit/net/sysconfig.py
+++ /dev/null
@@ -1,400 +0,0 @@
-# vi: ts=4 expandtab
-#
-# 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 os
-import re
-
-import six
-
-from cloudinit.distros.parsers import resolv_conf
-from cloudinit import util
-
-from . import renderer
-
-
-def _make_header(sep='#'):
- lines = [
- "Created by cloud-init on instance boot automatically, do not edit.",
- "",
- ]
- for i in range(0, len(lines)):
- if lines[i]:
- lines[i] = sep + " " + lines[i]
- else:
- lines[i] = sep
- return "\n".join(lines)
-
-
-def _is_default_route(route):
- if route['network'] == '::' and route['netmask'] == 0:
- return True
- if route['network'] == '0.0.0.0' and route['netmask'] == '0.0.0.0':
- return True
- return False
-
-
-def _quote_value(value):
- if re.search(r"\s", value):
- # This doesn't handle complex cases...
- if value.startswith('"') and value.endswith('"'):
- return value
- else:
- return '"%s"' % value
- else:
- return value
-
-
-class ConfigMap(object):
- """Sysconfig like dictionary object."""
-
- # Why does redhat prefer yes/no to true/false??
- _bool_map = {
- True: 'yes',
- False: 'no',
- }
-
- def __init__(self):
- self._conf = {}
-
- def __setitem__(self, key, value):
- self._conf[key] = value
-
- def drop(self, key):
- self._conf.pop(key, None)
-
- def __len__(self):
- return len(self._conf)
-
- def to_string(self):
- buf = six.StringIO()
- buf.write(_make_header())
- if self._conf:
- buf.write("\n")
- for key in sorted(self._conf.keys()):
- value = self._conf[key]
- if isinstance(value, bool):
- value = self._bool_map[value]
- if not isinstance(value, six.string_types):
- value = str(value)
- buf.write("%s=%s\n" % (key, _quote_value(value)))
- return buf.getvalue()
-
-
-class Route(ConfigMap):
- """Represents a route configuration."""
-
- route_fn_tpl = '%(base)s/network-scripts/route-%(name)s'
-
- def __init__(self, route_name, base_sysconf_dir):
- super(Route, self).__init__()
- self.last_idx = 1
- self.has_set_default = False
- self._route_name = route_name
- self._base_sysconf_dir = base_sysconf_dir
-
- def copy(self):
- r = Route(self._route_name, self._base_sysconf_dir)
- r._conf = self._conf.copy()
- r.last_idx = self.last_idx
- r.has_set_default = self.has_set_default
- return r
-
- @property
- def path(self):
- return self.route_fn_tpl % ({'base': self._base_sysconf_dir,
- 'name': self._route_name})
-
-
-class NetInterface(ConfigMap):
- """Represents a sysconfig/networking-script (and its config + children)."""
-
- iface_fn_tpl = '%(base)s/network-scripts/ifcfg-%(name)s'
-
- iface_types = {
- 'ethernet': 'Ethernet',
- 'bond': 'Bond',
- 'bridge': 'Bridge',
- }
-
- def __init__(self, iface_name, base_sysconf_dir, kind='ethernet'):
- super(NetInterface, self).__init__()
- self.children = []
- self.routes = Route(iface_name, base_sysconf_dir)
- self._kind = kind
- self._iface_name = iface_name
- self._conf['DEVICE'] = iface_name
- self._conf['TYPE'] = self.iface_types[kind]
- self._base_sysconf_dir = base_sysconf_dir
-
- @property
- def name(self):
- return self._iface_name
-
- @name.setter
- def name(self, iface_name):
- self._iface_name = iface_name
- self._conf['DEVICE'] = iface_name
-
- @property
- def kind(self):
- return self._kind
-
- @kind.setter
- def kind(self, kind):
- self._kind = kind
- self._conf['TYPE'] = self.iface_types[kind]
-
- @property
- def path(self):
- return self.iface_fn_tpl % ({'base': self._base_sysconf_dir,
- 'name': self.name})
-
- def copy(self, copy_children=False, copy_routes=False):
- c = NetInterface(self.name, self._base_sysconf_dir, kind=self._kind)
- c._conf = self._conf.copy()
- if copy_children:
- c.children = list(self.children)
- if copy_routes:
- c.routes = self.routes.copy()
- return c
-
-
-class Renderer(renderer.Renderer):
- """Renders network information in a /etc/sysconfig format."""
-
- # See: https://access.redhat.com/documentation/en-US/\
- # Red_Hat_Enterprise_Linux/6/html/Deployment_Guide/\
- # s1-networkscripts-interfaces.html (or other docs for
- # details about this)
-
- iface_defaults = tuple([
- ('ONBOOT', True),
- ('USERCTL', False),
- ('NM_CONTROLLED', False),
- ('BOOTPROTO', 'none'),
- ])
-
- # If these keys exist, then there values will be used to form
- # a BONDING_OPTS grouping; otherwise no grouping will be set.
- bond_tpl_opts = tuple([
- ('bond_mode', "mode=%s"),
- ('bond_xmit_hash_policy', "xmit_hash_policy=%s"),
- ('bond_miimon', "miimon=%s"),
- ])
-
- bridge_opts_keys = tuple([
- ('bridge_stp', 'STP'),
- ('bridge_ageing', 'AGEING'),
- ('bridge_bridgeprio', 'PRIO'),
- ])
-
- def __init__(self, config=None):
- if not config:
- config = {}
- self.sysconf_dir = config.get('sysconf_dir', 'etc/sysconfig/')
- self.netrules_path = config.get(
- 'netrules_path', 'etc/udev/rules.d/70-persistent-net.rules')
- self.dns_path = config.get('dns_path', 'etc/resolv.conf')
-
- @classmethod
- def _render_iface_shared(cls, iface, iface_cfg):
- for k, v in cls.iface_defaults:
- iface_cfg[k] = v
- for (old_key, new_key) in [('mac_address', 'HWADDR'), ('mtu', 'MTU')]:
- old_value = iface.get(old_key)
- if old_value is not None:
- iface_cfg[new_key] = old_value
-
- @classmethod
- def _render_subnet(cls, iface_cfg, route_cfg, subnet):
- subnet_type = subnet.get('type')
- if subnet_type == 'dhcp6':
- iface_cfg['DHCPV6C'] = True
- iface_cfg['IPV6INIT'] = True
- iface_cfg['BOOTPROTO'] = 'dhcp'
- elif subnet_type in ['dhcp4', 'dhcp']:
- iface_cfg['BOOTPROTO'] = 'dhcp'
- elif subnet_type == 'static':
- iface_cfg['BOOTPROTO'] = 'static'
- if subnet.get('ipv6'):
- iface_cfg['IPV6ADDR'] = subnet['address']
- iface_cfg['IPV6INIT'] = True
- else:
- iface_cfg['IPADDR'] = subnet['address']
- else:
- raise ValueError("Unknown subnet type '%s' found"
- " for interface '%s'" % (subnet_type,
- iface_cfg.name))
- if 'netmask' in subnet:
- iface_cfg['NETMASK'] = subnet['netmask']
- for route in subnet.get('routes', []):
- if _is_default_route(route):
- if route_cfg.has_set_default:
- raise ValueError("Duplicate declaration of default"
- " route found for interface '%s'"
- % (iface_cfg.name))
- # NOTE(harlowja): ipv6 and ipv4 default gateways
- gw_key = 'GATEWAY0'
- nm_key = 'NETMASK0'
- addr_key = 'ADDRESS0'
- # The owning interface provides the default route.
- #
- # TODO(harlowja): add validation that no other iface has
- # also provided the default route?
- iface_cfg['DEFROUTE'] = True
- if 'gateway' in route:
- iface_cfg['GATEWAY'] = route['gateway']
- route_cfg.has_set_default = True
- else:
- gw_key = 'GATEWAY%s' % route_cfg.last_idx
- nm_key = 'NETMASK%s' % route_cfg.last_idx
- addr_key = 'ADDRESS%s' % route_cfg.last_idx
- route_cfg.last_idx += 1
- for (old_key, new_key) in [('gateway', gw_key),
- ('netmask', nm_key),
- ('network', addr_key)]:
- if old_key in route:
- route_cfg[new_key] = route[old_key]
-
- @classmethod
- def _render_bonding_opts(cls, iface_cfg, iface):
- bond_opts = []
- for (bond_key, value_tpl) in cls.bond_tpl_opts:
- # Seems like either dash or underscore is possible?
- bond_keys = [bond_key, bond_key.replace("_", "-")]
- for bond_key in bond_keys:
- if bond_key in iface:
- bond_value = iface[bond_key]
- if isinstance(bond_value, (tuple, list)):
- bond_value = " ".join(bond_value)
- bond_opts.append(value_tpl % (bond_value))
- break
- if bond_opts:
- iface_cfg['BONDING_OPTS'] = " ".join(bond_opts)
-
- @classmethod
- def _render_physical_interfaces(cls, network_state, iface_contents):
- physical_filter = renderer.filter_by_physical
- for iface in network_state.iter_interfaces(physical_filter):
- iface_name = iface['name']
- iface_subnets = iface.get("subnets", [])
- iface_cfg = iface_contents[iface_name]
- route_cfg = iface_cfg.routes
- if len(iface_subnets) == 1:
- 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)):
- iface_sub_cfg = iface_cfg.copy()
- iface_sub_cfg.name = "%s:%s" % (iface_name, i)
- iface.children.append(iface_sub_cfg)
- cls._render_subnet(iface_sub_cfg, route_cfg, iface_subnet)
-
- @classmethod
- def _render_bond_interfaces(cls, network_state, iface_contents):
- bond_filter = renderer.filter_by_type('bond')
- for iface in network_state.iter_interfaces(bond_filter):
- iface_name = iface['name']
- iface_cfg = iface_contents[iface_name]
- cls._render_bonding_opts(iface_cfg, iface)
- iface_master_name = iface['bond-master']
- iface_cfg['MASTER'] = iface_master_name
- iface_cfg['SLAVE'] = True
- # Ensure that the master interface (and any of its children)
- # are actually marked as being bond types...
- master_cfg = iface_contents[iface_master_name]
- master_cfgs = [master_cfg]
- master_cfgs.extend(master_cfg.children)
- for master_cfg in master_cfgs:
- master_cfg['BONDING_MASTER'] = True
- master_cfg.kind = 'bond'
-
- @staticmethod
- def _render_vlan_interfaces(network_state, iface_contents):
- vlan_filter = renderer.filter_by_type('vlan')
- for iface in network_state.iter_interfaces(vlan_filter):
- iface_name = iface['name']
- iface_cfg = iface_contents[iface_name]
- iface_cfg['VLAN'] = True
- iface_cfg['PHYSDEV'] = iface_name[:iface_name.rfind('.')]
-
- @staticmethod
- def _render_dns(network_state, existing_dns_path=None):
- content = resolv_conf.ResolvConf("")
- if existing_dns_path and os.path.isfile(existing_dns_path):
- content = resolv_conf.ResolvConf(util.load_file(existing_dns_path))
- for nameserver in network_state.dns_nameservers:
- content.add_nameserver(nameserver)
- for searchdomain in network_state.dns_searchdomains:
- content.add_search_domain(searchdomain)
- return "\n".join([_make_header(';'), str(content)])
-
- @classmethod
- def _render_bridge_interfaces(cls, network_state, iface_contents):
- bridge_filter = renderer.filter_by_type('bridge')
- for iface in network_state.iter_interfaces(bridge_filter):
- iface_name = iface['name']
- iface_cfg = iface_contents[iface_name]
- iface_cfg.kind = 'bridge'
- for old_key, new_key in cls.bridge_opts_keys:
- if old_key in iface:
- iface_cfg[new_key] = iface[old_key]
- # Is this the right key to get all the connected interfaces?
- for bridged_iface_name in iface.get('bridge_ports', []):
- # Ensure all bridged interfaces are correctly tagged
- # as being bridged to this interface.
- bridged_cfg = iface_contents[bridged_iface_name]
- bridged_cfgs = [bridged_cfg]
- bridged_cfgs.extend(bridged_cfg.children)
- for bridge_cfg in bridged_cfgs:
- bridge_cfg['BRIDGE'] = iface_name
-
- @classmethod
- def _render_sysconfig(cls, base_sysconf_dir, network_state):
- '''Given state, return /etc/sysconfig files + contents'''
- iface_contents = {}
- for iface in network_state.iter_interfaces():
- iface_name = iface['name']
- iface_cfg = NetInterface(iface_name, base_sysconf_dir)
- cls._render_iface_shared(iface, iface_cfg)
- iface_contents[iface_name] = iface_cfg
- cls._render_physical_interfaces(network_state, iface_contents)
- cls._render_bond_interfaces(network_state, iface_contents)
- cls._render_vlan_interfaces(network_state, iface_contents)
- cls._render_bridge_interfaces(network_state, iface_contents)
- contents = {}
- for iface_name, iface_cfg in iface_contents.items():
- if iface_cfg or iface_cfg.children:
- contents[iface_cfg.path] = iface_cfg.to_string()
- for iface_cfg in iface_cfg.children:
- if iface_cfg:
- contents[iface_cfg.path] = iface_cfg.to_string()
- if iface_cfg.routes:
- contents[iface_cfg.routes.path] = iface_cfg.routes.to_string()
- return contents
-
- def render_network_state(self, target, network_state):
- base_sysconf_dir = os.path.join(target, self.sysconf_dir)
- for path, data in self._render_sysconfig(base_sysconf_dir,
- network_state).items():
- util.write_file(path, data)
- if self.dns_path:
- dns_path = os.path.join(target, self.dns_path)
- resolv_content = self._render_dns(network_state,
- existing_dns_path=dns_path)
- util.write_file(dns_path, resolv_content)
- if self.netrules_path:
- netrules_content = self._render_persistent_net(network_state)
- netrules_path = os.path.join(target, self.netrules_path)
- util.write_file(netrules_path, netrules_content)