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 Currently the parsed object is dumped into a bash global definition file
40 for deploy.sh consumption. This object will later be used directly as
41 deployment script move to python.
43 def __init__(self, filename):
45 if isinstance(filename, str):
46 with open(filename, 'r') as network_settings_file:
47 init_dict = yaml.safe_load(network_settings_file)
49 # assume input is a dict to build from
51 super().__init__(init_dict)
54 # merge two dicts Non-destructively
56 for key, val in sec.items():
58 if isinstance(val, dict):
61 # do not overwrite what's already there
64 # merge the apex specific config into the first class settings
65 merge(self, copy(self['apex']))
67 self.enabled_network_list = []
68 self.nics = {COMPUTE: {}, CONTROLLER: {}}
69 self.nics_specified = {COMPUTE: False, CONTROLLER: False}
70 self._validate_input()
72 def get_network(self, network):
73 if network == EXTERNAL_NETWORK and self['networks'][network]:
74 for net in self['networks'][network]:
78 raise NetworkSettingsException("The external network, "
79 "'public', should be defined "
80 "when external networks are "
83 return self['networks'][network]
85 def _validate_input(self):
87 Validates the network settings file and populates all fields.
89 NetworkSettingsException will be raised if validation fails.
91 if not self['networks'].get(ADMIN_NETWORK, {}).get('enabled', False):
92 raise NetworkSettingsException("You must enable admin network "
93 "and configure it explicitly or "
96 for network in OPNFV_NETWORK_TYPES:
97 if network in self['networks']:
98 _network = self.get_network(network)
99 if _network.get('enabled', True):
100 logging.info("{} enabled".format(network))
101 self._config_required_settings(network)
102 nicmap = _network['nic_mapping']
103 self._validate_overcloud_nic_order(network)
104 iface = nicmap[CONTROLLER]['members'][0]
105 self._config_ip_range(network=network,
107 ip_range='overcloud_ip_range',
108 start_offset=21, end_offset=21)
109 self.enabled_network_list.append(network)
110 # TODO self._config_optional_settings(network)
112 logging.info("{} disabled, will collapse with "
113 "admin network".format(network))
115 logging.info("{} is not in specified, will collapse with "
116 "admin network".format(network))
118 if 'dns-domain' not in self:
119 self['domain_name'] = DOMAIN_NAME
121 self['domain_name'] = self['dns-domain']
122 self['dns_servers'] = self.get('dns_nameservers', DNS_SERVERS)
123 self['ntp_servers'] = self.get('ntp', NTP_SERVER)
125 def _validate_overcloud_nic_order(self, network):
127 Detects if nic order is specified per profile (compute/controller)
130 If nic order is specified in a network for a profile, it should be
131 specified for every network with that profile other than admin network
133 Duplicate nic names are also not allowed across different networks
135 :param network: network to detect if nic order present
139 _network = self.get_network(network)
140 _nicmap = _network.get('nic_mapping', {})
141 _role = _nicmap.get(role, {})
142 interfaces = _role.get('members', [])
145 interface = interfaces[0]
146 if not isinstance(_role.get('vlan', 'native'), int) and \
147 any(y == interface for x, y in self.nics[role].items()):
148 raise NetworkSettingsException(
149 "Duplicate {} already specified for "
150 "another network".format(interface))
151 self.nics[role][network] = interface
152 self.nics_specified[role] = True
153 logging.info("{} nic order specified for network {"
154 "}".format(role, network))
156 raise NetworkSettingsException(
157 "Interface members are not supplied for {} network "
158 "for the {} role. Please add nic assignments"
159 "".format(network, role))
161 def _config_required_settings(self, network):
163 Configures either CIDR or bridged_interface setting
165 cidr takes precedence if both cidr and bridged_interface are specified
168 When using bridged_interface, we will detect network setting on the
169 given NIC in the system. The resulting config in settings object will
170 be an ipaddress.network object, replacing the NIC name.
172 _network = self.get_network(network)
173 # if vlan not defined then default it to native
174 if network is not ADMIN_NETWORK:
176 if 'vlan' not in _network['nic_mapping'][role]:
177 _network['nic_mapping'][role]['vlan'] = 'native'
179 cidr = _network.get('cidr')
182 cidr = ipaddress.ip_network(_network['cidr'])
183 _network['cidr'] = cidr
184 logging.info("{}_cidr: {}".format(network, cidr))
185 elif 'installer_vm' in _network:
186 ucloud_if_list = _network['installer_vm']['members']
187 # If cidr is not specified, we need to know if we should find
188 # IPv6 or IPv4 address on the interface
189 ip = ipaddress.ip_address(_network['installer_vm']['ip'])
190 nic_if = ip_utils.get_interface(ucloud_if_list[0], ip.version)
192 logging.info("{}_bridged_interface: {}".
193 format(network, nic_if))
195 raise NetworkSettingsException(
196 "Auto detection failed for {}: Unable to find valid "
197 "ip for interface {}".format(network, ucloud_if_list[0]))
200 raise NetworkSettingsException(
201 "Auto detection failed for {}: either installer_vm "
202 "members or cidr must be specified".format(network))
204 # undercloud settings
205 if network == ADMIN_NETWORK:
206 provisioner_ip = _network['installer_vm']['ip']
207 iface = _network['installer_vm']['members'][0]
208 if not provisioner_ip:
209 _network['installer_vm']['ip'] = self._gen_ip(network, 1)
210 self._config_ip_range(network=network, interface=iface,
211 ip_range='dhcp_range',
212 start_offset=2, count=9)
213 self._config_ip_range(network=network, interface=iface,
214 ip_range='introspection_range',
215 start_offset=11, count=9)
216 elif network == EXTERNAL_NETWORK:
217 provisioner_ip = _network['installer_vm']['ip']
218 iface = _network['installer_vm']['members'][0]
219 if not provisioner_ip:
220 _network['installer_vm']['ip'] = self._gen_ip(network, 1)
221 self._config_ip_range(network=network, interface=iface,
222 ip_range='floating_ip_range',
223 end_offset=2, count=20)
225 gateway = _network['gateway']
226 interface = _network['installer_vm']['ip']
227 self._config_gateway(network, gateway, interface)
229 def _config_ip_range(self, network, ip_range, interface=None,
230 start_offset=None, end_offset=None, count=None):
232 Configures IP range for a given setting.
233 If the setting is already specified, no change will be made.
234 The spec for start_offset, end_offset and count are identical to
235 ip_utils.get_ip_range.
237 _network = self.get_network(network)
238 if ip_range not in _network:
239 cidr = _network.get('cidr')
240 _ip_range = ip_utils.get_ip_range(start_offset=start_offset,
241 end_offset=end_offset,
245 _network[ip_range] = _ip_range.split(',')
247 logging.info("Config IP Range: {} {}".format(network, ip_range))
249 def _gen_ip(self, network, offset):
251 Generate and ip offset within the given network
253 _network = self.get_network(network)
254 cidr = _network.get('cidr')
255 ip = ip_utils.get_ip(offset, cidr)
256 logging.info("Config IP: {} {}".format(network, ip))
259 def _config_optional_settings(self, network):
261 Configures optional settings:
265 - introspection_range
271 if network == ADMIN_NETWORK:
272 # FIXME: _config_ip function does not exist!
273 self._config_ip(network, None, 'provisioner_ip', 1)
274 self._config_ip_range(network=network,
275 ip_range='dhcp_range',
276 start_offset=2, count=9)
277 self._config_ip_range(network=network,
278 ip_range='introspection_range',
279 start_offset=11, count=9)
280 elif network == EXTERNAL_NETWORK:
281 # FIXME: _config_ip function does not exist!
282 self._config_ip(network, None, 'provisioner_ip', 1)
283 self._config_ip_range(network=network,
284 ip_range='floating_ip_range',
285 end_offset=2, count=20)
286 self._config_gateway(network)
288 def _config_gateway(self, network, gateway, interface):
290 Configures gateway setting for a given network.
292 If cidr is specified, we always use the first address in the address
293 space for gateway. Otherwise, we detect the system gateway.
295 _network = self.get_network(network)
297 cidr = _network.get('cidr')
299 _gateway = ip_utils.get_ip(1, cidr)
301 _gateway = ip_utils.find_gateway(interface)
304 _network['gateway'] = _gateway
306 raise NetworkSettingsException("Failed to set gateway")
308 logging.info("Config Gateway: {} {}".format(network, gateway))
310 def get_ip_addr_family(self,):
312 Returns IP address family for current deployment.
314 If any enabled network has IPv6 CIDR, the deployment is classified as
318 ipaddress.ip_network(self.get_network(n)['cidr']).version
319 for n in self.enabled_network_list])
322 class NetworkSettingsException(Exception):
323 def __init__(self, value):