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 ##############################################################################
13 from . import ip_utils
14 from .common import constants, utils
17 class NetworkSettings:
19 This class parses APEX network settings yaml file into an object. It
20 generates or detects all missing fields for deployment.
22 The resulting object will be used later to generate network environment
23 file as well as configuring post deployment networks.
25 Currently the parsed object is dumped into a bash global definition file
26 for deploy.sh consumption. This object will later be used directly as
27 deployment script move to python.
29 def __init__(self, filename, network_isolation):
30 with open(filename, 'r') as network_settings_file:
31 self.settings_obj = yaml.load(network_settings_file)
32 self.network_isolation = network_isolation
33 self.enabled_network_list = []
34 self.nics = {'compute': dict(), 'controller': dict()}
35 self.nics_specified = {'compute': False, 'controller': False}
36 self._validate_input()
38 def _validate_input(self):
40 Validates the network settings file and populates all fields.
42 NetworkSettingsException will be raised if validation fails.
44 if constants.ADMIN_NETWORK not in self.settings_obj or \
45 not utils.str2bool(self.settings_obj[constants.ADMIN_NETWORK].get(
47 raise NetworkSettingsException("You must enable admin_network "
48 "and configure it explicitly or "
50 if self.network_isolation and \
51 (constants.PUBLIC_NETWORK not in self.settings_obj or not
52 utils.str2bool(self.settings_obj[constants.PUBLIC_NETWORK].get(
54 raise NetworkSettingsException("You must enable public_network "
55 "and configure it explicitly or "
58 for network in constants.OPNFV_NETWORK_TYPES:
59 if network in self.settings_obj:
60 if utils.str2bool(self.settings_obj[network].get('enabled')):
61 logging.info("{} enabled".format(network))
62 self._config_required_settings(network)
63 self._config_ip_range(network=network,
64 setting='usable_ip_range',
65 start_offset=21, end_offset=21)
66 self._config_optional_settings(network)
67 self.enabled_network_list.append(network)
68 self._validate_overcloud_nic_order(network)
70 logging.info("{} disabled, will collapse with "
71 "admin_network".format(network))
73 logging.info("{} is not in specified, will collapse with "
74 "admin_network".format(network))
76 self.settings_obj['dns_servers'] = self.settings_obj.get(
77 'dns_servers', constants.DNS_SERVERS)
78 self.settings_obj['domain_name'] = self.settings_obj.get(
79 'domain_name', constants.DOMAIN_NAME)
81 def _validate_overcloud_nic_order(self, network):
83 Detects if nic order is specified per profile (compute/controller)
86 If nic order is specified in a network for a profile, it should be
87 specified for every network with that profile other than admin_network
89 Duplicate nic names are also not allowed across different networks
91 :param network: network to detect if nic order present
95 for role in constants.ROLES:
96 interface = role+'_interface'
97 nic_index = self.get_enabled_networks().index(network) + 1
98 if interface in self.settings_obj[network]:
99 if any(y == self.settings_obj[network][interface] for x, y in
100 self.nics[role].items()):
101 raise NetworkSettingsException("Duplicate {} already "
104 .format(self.settings_obj
107 self.nics[role][network] = self.settings_obj[network][
109 self.nics_specified[role] = True
110 logging.info("{} nic order specified for network {"
111 "}".format(role, network))
112 elif self.nics_specified[role]:
113 logging.error("{} nic order not specified for network {"
114 "}".format(role, network))
115 raise NetworkSettingsException("Must specify {} for all "
116 "enabled networks (other than "
117 " admin) or not specify it for "
118 "any".format(interface))
120 logging.info("{} nic order not specified for network {"
121 "}. Will use logical default "
122 "nic{}".format(interface, network, nic_index))
123 self.nics[role][network] = 'nic' + str(nic_index)
126 def _config_required_settings(self, network):
128 Configures either CIDR or bridged_interface setting
130 cidr takes precedence if both cidr and bridged_interface are specified
133 When using bridged_interface, we will detect network setting on the
134 given NIC in the system. The resulting config in settings object will
135 be an ipaddress.network object, replacing the NIC name.
137 # if vlan not defined then default it to native
138 if network is not constants.ADMIN_NETWORK:
139 if 'vlan' not in self.settings_obj[network]:
140 self.settings_obj[network]['vlan'] = 'native'
142 cidr = self.settings_obj[network].get('cidr')
143 nic_name = self.settings_obj[network].get('bridged_interface')
146 cidr = ipaddress.ip_network(self.settings_obj[network]['cidr'])
147 self.settings_obj[network]['cidr'] = cidr
148 logging.info("{}_cidr: {}".format(network, cidr))
151 # If cidr is not specified, we need to know if we should find
152 # IPv6 or IPv4 address on the interface
153 if utils.str2bool(self.settings_obj[network].get('ipv6')):
157 nic_interface = ip_utils.get_interface(nic_name, address_family)
159 self.settings_obj[network]['bridged_interface'] = nic_interface
160 logging.info("{}_bridged_interface: {}".
161 format(network, nic_interface))
164 raise NetworkSettingsException("Auto detection failed for {}: "
165 "Unable to find valid ip for "
167 .format(network, nic_name))
170 raise NetworkSettingsException("Auto detection failed for {}: "
171 "either bridge_interface or cidr "
175 def _config_ip_range(self, network, setting, start_offset=None,
176 end_offset=None, count=None):
178 Configures IP range for a given setting.
180 If the setting is already specified, no change will be made.
182 The spec for start_offset, end_offset and count are identical to
183 ip_utils.get_ip_range.
185 ip_range = self.settings_obj[network].get(setting)
186 interface = self.settings_obj[network].get('bridged_interface')
189 cidr = self.settings_obj[network].get('cidr')
190 ip_range = ip_utils.get_ip_range(start_offset=start_offset,
191 end_offset=end_offset,
195 self.settings_obj[network][setting] = ip_range
197 logging.info("{}_{}: {}".format(network, setting, ip_range))
199 def _config_ip(self, network, setting, offset):
201 Configures IP for a given setting.
203 If the setting is already specified, no change will be made.
205 The spec for offset is identical to ip_utils.get_ip
207 ip = self.settings_obj[network].get(setting)
208 interface = self.settings_obj[network].get('bridged_interface')
211 cidr = self.settings_obj[network].get('cidr')
212 ip = ip_utils.get_ip(offset, cidr, interface)
213 self.settings_obj[network][setting] = ip
215 logging.info("{}_{}: {}".format(network, setting, ip))
217 def _config_optional_settings(self, network):
219 Configures optional settings:
223 - introspection_range
229 if network == constants.ADMIN_NETWORK:
230 self._config_ip(network, 'provisioner_ip', 1)
231 self._config_ip_range(network=network, setting='dhcp_range',
232 start_offset=2, count=9)
233 self._config_ip_range(network=network,
234 setting='introspection_range',
235 start_offset=11, count=9)
236 elif network == constants.PUBLIC_NETWORK:
237 self._config_ip(network, 'provisioner_ip', 1)
238 self._config_ip_range(network=network,
239 setting='floating_ip_range',
240 end_offset=2, count=20)
241 self._config_gateway(network)
243 def _config_gateway(self, network):
245 Configures gateway setting for a given network.
247 If cidr is specified, we always use the first address in the address
248 space for gateway. Otherwise, we detect the system gateway.
250 gateway = self.settings_obj[network].get('gateway')
251 interface = self.settings_obj[network].get('bridged_interface')
254 cidr = self.settings_obj[network].get('cidr')
256 gateway = ip_utils.get_ip(1, cidr)
258 gateway = ip_utils.find_gateway(interface)
261 self.settings_obj[network]['gateway'] = gateway
263 raise NetworkSettingsException("Failed to set gateway")
265 logging.info("{}_gateway: {}".format(network, gateway))
267 def dump_bash(self, path=None):
269 Prints settings for bash consumption.
271 If optional path is provided, bash string will be written to the file
275 for network in self.enabled_network_list:
276 for key, value in self.settings_obj[network].items():
277 bash_str += "{}_{}={}\n".format(network, key, value)
278 bash_str += "enabled_network_list='{}'\n" \
279 .format(' '.join(self.enabled_network_list))
280 bash_str += "ip_addr_family={}\n".format(self.get_ip_addr_family())
282 for dns_server in self.settings_obj['dns_servers']:
283 dns_list = dns_list + "{} ".format(dns_server)
284 dns_list = dns_list.strip()
285 bash_str += "dns_servers=\'{}\'\n".format(dns_list)
286 bash_str += "domain_name=\'{}\'\n".format(self.settings_obj[
289 with open(path, 'w') as file:
294 def get_ip_addr_family(self):
296 Returns IP address family for current deployment.
298 If any enabled network has IPv6 CIDR, the deployment is classified as
301 for network in self.enabled_network_list:
302 cidr = ipaddress.ip_network(self.settings_obj[network]['cidr'])
303 if cidr.version == 6:
308 def get_network_settings(self):
310 Getter for network settings
311 :return: network settings dictionary
313 return self.settings_obj
315 def get_enabled_networks(self):
317 Getter for enabled network list
318 :return: list of enabled networks
320 return self.enabled_network_list
323 class NetworkSettingsException(Exception):
324 def __init__(self, value):