1 ##############################################################################
2 # Copyright (c) 2016 Feng Pan (fpan@redhat.com) and others.
4 # All rights reserved. This program and the accompanying materials
5 # are made available under the terms of the Apache License, Version 2.0
6 # which accompanies this distribution, and is available at
7 # http://www.apache.org/licenses/LICENSE-2.0
8 ##############################################################################
16 from apex.common import utils
17 from apex.common.constants import (
28 from apex.network import ip_utils
31 class NetworkSettings(dict):
33 This class parses APEX network settings yaml file into an object. It
34 generates or detects all missing fields for deployment.
36 The resulting object will be used later to generate network environment
37 file as well as configuring post deployment networks.
39 def __init__(self, filename):
41 if isinstance(filename, str):
42 with open(filename, 'r') as network_settings_file:
43 init_dict = yaml.safe_load(network_settings_file)
45 # assume input is a dict to build from
47 super().__init__(init_dict)
50 # merge two dicts Non-destructively
52 for key, val in sec.items():
54 if isinstance(val, dict):
57 # do not overwrite what's already there
60 # merge the apex specific config into the first class settings
61 merge(self, copy(self['apex']))
63 self.enabled_network_list = []
64 self.nics = {COMPUTE: {}, CONTROLLER: {}}
65 self.nics_specified = {COMPUTE: False, CONTROLLER: False}
66 self._validate_input()
68 def get_network(self, network):
69 if network == EXTERNAL_NETWORK and self['networks'][network]:
70 for net in self['networks'][network]:
74 raise NetworkSettingsException("The external network, "
75 "'public', should be defined "
76 "when external networks are "
79 return self['networks'][network]
81 def _validate_input(self):
83 Validates the network settings file and populates all fields.
85 NetworkSettingsException will be raised if validation fails.
87 if not self['networks'].get(ADMIN_NETWORK, {}).get('enabled', False):
88 raise NetworkSettingsException("You must enable admin network "
89 "and configure it explicitly or "
92 for network in OPNFV_NETWORK_TYPES:
93 if network in self['networks']:
94 _network = self.get_network(network)
95 if _network.get('enabled', True):
96 logging.info("{} enabled".format(network))
97 self._config_required_settings(network)
98 nicmap = _network['nic_mapping']
99 self._validate_overcloud_nic_order(network)
100 iface = nicmap[CONTROLLER]['members'][0]
101 self._config_ip_range(network=network,
103 ip_range='overcloud_ip_range',
104 start_offset=21, end_offset=21)
105 self.enabled_network_list.append(network)
106 # TODO self._config_optional_settings(network)
108 logging.info("{} disabled, will collapse with "
109 "admin network".format(network))
111 logging.info("{} is not in specified, will collapse with "
112 "admin network".format(network))
114 if 'dns-domain' not in self:
115 self['domain_name'] = DOMAIN_NAME
117 self['domain_name'] = self['dns-domain']
118 self['dns_servers'] = self.get('dns_nameservers', DNS_SERVERS)
119 self['ntp_servers'] = self.get('ntp', NTP_SERVER)
121 def _validate_overcloud_nic_order(self, network):
123 Detects if nic order is specified per profile (compute/controller)
126 If nic order is specified in a network for a profile, it should be
127 specified for every network with that profile other than admin network
129 Duplicate nic names are also not allowed across different networks
131 :param network: network to detect if nic order present
135 _network = self.get_network(network)
136 _nicmap = _network.get('nic_mapping', {})
137 _role = _nicmap.get(role, {})
138 interfaces = _role.get('members', [])
141 interface = interfaces[0]
142 if not isinstance(_role.get('vlan', 'native'), int) and \
143 any(y == interface for x, y in self.nics[role].items()):
144 raise NetworkSettingsException(
145 "Duplicate {} already specified for "
146 "another network".format(interface))
147 self.nics[role][network] = interface
148 self.nics_specified[role] = True
149 logging.info("{} nic order specified for network {"
150 "}".format(role, network))
152 raise NetworkSettingsException(
153 "Interface members are not supplied for {} network "
154 "for the {} role. Please add nic assignments"
155 "".format(network, role))
157 def _config_required_settings(self, network):
159 Configures either CIDR or bridged_interface setting
161 cidr takes precedence if both cidr and bridged_interface are specified
164 When using bridged_interface, we will detect network setting on the
165 given NIC in the system. The resulting config in settings object will
166 be an ipaddress.network object, replacing the NIC name.
168 _network = self.get_network(network)
169 # if vlan not defined then default it to native
170 if network is not ADMIN_NETWORK:
172 if 'vlan' not in _network['nic_mapping'][role]:
173 _network['nic_mapping'][role]['vlan'] = 'native'
175 cidr = _network.get('cidr')
178 cidr = ipaddress.ip_network(_network['cidr'])
179 _network['cidr'] = cidr
180 logging.info("{}_cidr: {}".format(network, cidr))
181 elif 'installer_vm' in _network:
182 ucloud_if_list = _network['installer_vm']['members']
183 # If cidr is not specified, we need to know if we should find
184 # IPv6 or IPv4 address on the interface
185 ip = ipaddress.ip_address(_network['installer_vm']['ip'])
186 nic_if = ip_utils.get_interface(ucloud_if_list[0], ip.version)
188 logging.info("{}_bridged_interface: {}".
189 format(network, nic_if))
191 raise NetworkSettingsException(
192 "Auto detection failed for {}: Unable to find valid "
193 "ip for interface {}".format(network, ucloud_if_list[0]))
196 raise NetworkSettingsException(
197 "Auto detection failed for {}: either installer_vm "
198 "members or cidr must be specified".format(network))
200 # undercloud settings
201 if network == ADMIN_NETWORK:
202 provisioner_ip = _network['installer_vm']['ip']
203 iface = _network['installer_vm']['members'][0]
204 if not provisioner_ip:
205 _network['installer_vm']['ip'] = self._gen_ip(network, 1)
206 self._config_ip_range(network=network, interface=iface,
207 ip_range='dhcp_range',
208 start_offset=2, count=9)
209 self._config_ip_range(network=network, interface=iface,
210 ip_range='introspection_range',
211 start_offset=11, count=9)
212 elif network == EXTERNAL_NETWORK:
213 provisioner_ip = _network['installer_vm']['ip']
214 iface = _network['installer_vm']['members'][0]
215 if not provisioner_ip:
216 _network['installer_vm']['ip'] = self._gen_ip(network, 1)
217 self._config_ip_range(network=network, interface=iface,
218 ip_range='floating_ip_range',
219 end_offset=2, count=20)
221 gateway = _network['gateway']
222 interface = _network['installer_vm']['ip']
223 self._config_gateway(network, gateway, interface)
225 def _config_ip_range(self, network, ip_range, interface=None,
226 start_offset=None, end_offset=None, count=None):
228 Configures IP range for a given setting.
229 If the setting is already specified, no change will be made.
230 The spec for start_offset, end_offset and count are identical to
231 ip_utils.get_ip_range.
233 _network = self.get_network(network)
234 if ip_range not in _network:
235 cidr = _network.get('cidr')
236 _ip_range = ip_utils.get_ip_range(start_offset=start_offset,
237 end_offset=end_offset,
241 _network[ip_range] = _ip_range.split(',')
243 logging.info("Config IP Range: {} {}".format(network, ip_range))
245 def _gen_ip(self, network, offset):
247 Generate and ip offset within the given network
249 _network = self.get_network(network)
250 cidr = _network.get('cidr')
251 ip = ip_utils.get_ip(offset, cidr)
252 logging.info("Config IP: {} {}".format(network, ip))
255 def _config_optional_settings(self, network):
257 Configures optional settings:
261 - introspection_range
267 if network == ADMIN_NETWORK:
268 # FIXME: _config_ip function does not exist!
269 self._config_ip(network, None, 'provisioner_ip', 1)
270 self._config_ip_range(network=network,
271 ip_range='dhcp_range',
272 start_offset=2, count=9)
273 self._config_ip_range(network=network,
274 ip_range='introspection_range',
275 start_offset=11, count=9)
276 elif network == EXTERNAL_NETWORK:
277 # FIXME: _config_ip function does not exist!
278 self._config_ip(network, None, 'provisioner_ip', 1)
279 self._config_ip_range(network=network,
280 ip_range='floating_ip_range',
281 end_offset=2, count=20)
282 self._config_gateway(network)
284 def _config_gateway(self, network, gateway, interface):
286 Configures gateway setting for a given network.
288 If cidr is specified, we always use the first address in the address
289 space for gateway. Otherwise, we detect the system gateway.
291 _network = self.get_network(network)
293 cidr = _network.get('cidr')
295 _gateway = ip_utils.get_ip(1, cidr)
297 _gateway = ip_utils.find_gateway(interface)
300 _network['gateway'] = _gateway
302 raise NetworkSettingsException("Failed to set gateway")
304 logging.info("Config Gateway: {} {}".format(network, gateway))
306 def get_ip_addr_family(self,):
308 Returns IP address family for current deployment.
310 If any enabled network has IPv6 CIDR, the deployment is classified as
314 ipaddress.ip_network(self.get_network(n)['cidr']).version
315 for n in self.enabled_network_list])
318 class NetworkSettingsException(Exception):
319 def __init__(self, value):