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
171 if network is not ADMIN_NETWORK:
172 if 'vlan' not in _network['nic_mapping'][role]:
173 _network['nic_mapping'][role]['vlan'] = 'native'
175 # ctlplane network must be native
176 _network['nic_mapping'][role]['vlan'] = 'native'
178 cidr = _network.get('cidr')
181 cidr = ipaddress.ip_network(_network['cidr'])
182 _network['cidr'] = cidr
183 logging.info("{}_cidr: {}".format(network, cidr))
184 elif 'installer_vm' in _network:
185 ucloud_if_list = _network['installer_vm']['members']
186 # If cidr is not specified, we need to know if we should find
187 # IPv6 or IPv4 address on the interface
188 ip = ipaddress.ip_address(_network['installer_vm']['ip'])
189 nic_if = ip_utils.get_interface(ucloud_if_list[0], ip.version)
191 logging.info("{}_bridged_interface: {}".
192 format(network, nic_if))
194 raise NetworkSettingsException(
195 "Auto detection failed for {}: Unable to find valid "
196 "ip for interface {}".format(network, ucloud_if_list[0]))
199 raise NetworkSettingsException(
200 "Auto detection failed for {}: either installer_vm "
201 "members or cidr must be specified".format(network))
203 # undercloud settings
204 if network == ADMIN_NETWORK:
205 provisioner_ip = _network['installer_vm']['ip']
206 iface = _network['installer_vm']['members'][0]
207 if not provisioner_ip:
208 _network['installer_vm']['ip'] = self._gen_ip(network, 1)
209 self._config_ip_range(network=network, interface=iface,
210 ip_range='dhcp_range',
211 start_offset=2, count=9)
212 self._config_ip_range(network=network, interface=iface,
213 ip_range='introspection_range',
214 start_offset=11, count=9)
215 elif network == EXTERNAL_NETWORK:
216 provisioner_ip = _network['installer_vm']['ip']
217 iface = _network['installer_vm']['members'][0]
218 if not provisioner_ip:
219 _network['installer_vm']['ip'] = self._gen_ip(network, 1)
220 self._config_ip_range(network=network, interface=iface,
221 ip_range='floating_ip_range',
222 end_offset=2, count=20)
224 gateway = _network['gateway']
225 interface = _network['installer_vm']['ip']
226 self._config_gateway(network, gateway, interface)
228 def _config_ip_range(self, network, ip_range, interface=None,
229 start_offset=None, end_offset=None, count=None):
231 Configures IP range for a given setting.
232 If the setting is already specified, no change will be made.
233 The spec for start_offset, end_offset and count are identical to
234 ip_utils.get_ip_range.
236 _network = self.get_network(network)
237 if ip_range not in _network:
238 cidr = _network.get('cidr')
239 _ip_range = ip_utils.get_ip_range(start_offset=start_offset,
240 end_offset=end_offset,
244 _network[ip_range] = _ip_range.split(',')
246 logging.info("Config IP Range: {} {}".format(network, ip_range))
248 def _gen_ip(self, network, offset):
250 Generate and ip offset within the given network
252 _network = self.get_network(network)
253 cidr = _network.get('cidr')
254 ip = ip_utils.get_ip(offset, cidr)
255 logging.info("Config IP: {} {}".format(network, ip))
258 def _config_optional_settings(self, network):
260 Configures optional settings:
264 - introspection_range
270 if network == ADMIN_NETWORK:
271 # FIXME: _config_ip function does not exist!
272 self._config_ip(network, None, 'provisioner_ip', 1)
273 self._config_ip_range(network=network,
274 ip_range='dhcp_range',
275 start_offset=2, count=9)
276 self._config_ip_range(network=network,
277 ip_range='introspection_range',
278 start_offset=11, count=9)
279 elif network == EXTERNAL_NETWORK:
280 # FIXME: _config_ip function does not exist!
281 self._config_ip(network, None, 'provisioner_ip', 1)
282 self._config_ip_range(network=network,
283 ip_range='floating_ip_range',
284 end_offset=2, count=20)
285 self._config_gateway(network)
287 def _config_gateway(self, network, gateway, interface):
289 Configures gateway setting for a given network.
291 If cidr is specified, we always use the first address in the address
292 space for gateway. Otherwise, we detect the system gateway.
294 _network = self.get_network(network)
296 cidr = _network.get('cidr')
298 _gateway = ip_utils.get_ip(1, cidr)
300 _gateway = ip_utils.find_gateway(interface)
303 _network['gateway'] = _gateway
305 raise NetworkSettingsException("Failed to set gateway")
307 logging.info("Config Gateway: {} {}".format(network, gateway))
309 def get_ip_addr_family(self,):
311 Returns IP address family for current deployment.
313 If any enabled network has IPv6 CIDR, the deployment is classified as
317 ipaddress.ip_network(self.get_network(n)['cidr']).version
318 for n in self.enabled_network_list])
321 class NetworkSettingsException(Exception):
322 def __init__(self, value):