1 ##############################################################################
2 # Copyright (c) 2017 Tim Rozet (trozet@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 ##############################################################################
15 from apex.common.exceptions import JumpHostNetworkException
16 from apex.common import parsers
17 from apex.network import ip_utils
21 'tenant': 'br-tenant',
22 'external': 'br-external',
23 'storage': 'br-storage',
27 NET_CFG_PATH = '/etc/sysconfig/network-scripts'
30 def configure_bridges(ns):
32 Configures IP on jumphost bridges
33 :param ns: network_settings
36 bridge_networks = ['admin']
37 if 'external' in ns.enabled_network_list:
38 bridge_networks.append('external')
39 for network in bridge_networks:
40 if network == 'external':
41 net_config = ns['networks'][network][0]
43 net_config = ns['networks'][network]
44 cidr = net_config['cidr']
45 interface = ip_utils.get_interface(NET_MAP[network], cidr.version)
48 logging.info("Bridge {} already configured with IP: {}".format(
49 NET_MAP[network], interface.ip))
51 logging.info("Will configure IP for {}".format(NET_MAP[network]))
52 ovs_ip = net_config['overcloud_ip_range'][1]
54 ipv6_br_path = "/proc/sys/net/ipv6/conf/{}/disable_" \
55 "ipv6".format(NET_MAP[network])
56 with open(ipv6_br_path, 'w') as f:
59 ip_prefix = "{}/{}".format(ovs_ip, cidr.prefixlen)
60 subprocess.check_call(['ip', 'addr', 'add', ip_prefix, 'dev',
62 subprocess.check_call(['ip', 'link', 'set', 'up', NET_MAP[
64 logging.info("IP configured: {} on bridge {}".format(ovs_ip,
66 except subprocess.CalledProcessError:
67 logging.error("Unable to configure IP address on "
68 "bridge {}".format(NET_MAP[network]))
72 def generate_ifcfg_params(if_file, network):
74 Generates and validates ifcfg parameters required for a network
75 :param if_file: ifcfg file to parse
76 :param network: Apex network
77 :return: dictionary of generated/validated ifcfg params
79 ifcfg_params = parsers.parse_ifcfg_file(if_file)
80 if not ifcfg_params['IPADDR']:
81 logging.error("IPADDR missing in {}".format(if_file))
82 raise JumpHostNetworkException("IPADDR missing in {}".format(if_file))
83 if not (ifcfg_params['NETMASK'] or ifcfg_params['PREFIX']):
84 logging.error("NETMASK/PREFIX missing in {}".format(if_file))
85 raise JumpHostNetworkException("NETMASK/PREFIX missing in {}".format(
87 if network == 'external' and not ifcfg_params['GATEWAY']:
88 logging.error("GATEWAY is required to be in {} for external "
89 "network".format(if_file))
90 raise JumpHostNetworkException("GATEWAY is required to be in {} for "
91 "external network".format(if_file))
93 if ifcfg_params['DNS1'] or ifcfg_params['DNS2']:
94 ifcfg_params['PEERDNS'] = 'yes'
96 ifcfg_params['PEERDNS'] = 'no'
100 def is_ovs_bridge(bridge):
103 :param bridge: OVS bridge to find
104 :return: boolean if OVS bridge exists
107 output = subprocess.check_output(['ovs-vsctl', 'show'],
108 stderr=subprocess.STDOUT)
109 if bridge not in output.decode('utf-8'):
110 logging.debug("Bridge {} not found".format(bridge))
113 logging.debug("Bridge {} found".format(bridge))
115 except subprocess.CalledProcessError:
116 logging.error("Unable to validate OVS bridge {}".format(bridge))
120 def dump_ovs_ports(bridge):
123 :param bridge: OVS bridge to list ports
124 :return: list of ports
127 output = subprocess.check_output(['ovs-vsctl', 'list-ports', bridge],
128 stderr=subprocess.STDOUT)
129 except subprocess.CalledProcessError:
130 logging.error("Unable to show ports for {}".format(bridge))
132 return output.decode('utf-8').strip().split('\n')
135 def attach_interface_to_ovs(bridge, interface, network):
137 Attaches jumphost interface to OVS for baremetal deployments
138 :param bridge: bridge to attach to
139 :param interface: interface to attach to bridge
140 :param network: Apex network type for these interfaces
144 if_file = os.path.join(NET_CFG_PATH, "ifcfg-{}".format(interface))
145 ovs_file = os.path.join(NET_CFG_PATH, "ifcfg-{}".format(bridge))
147 logging.info("Attaching interface: {} to bridge: {} on network {}".format(
148 bridge, interface, network
151 if not is_ovs_bridge(bridge):
152 subprocess.check_call(['ovs-vsctl', 'add-br', bridge])
153 elif interface in dump_ovs_ports(bridge):
154 logging.debug("Interface already attached to bridge")
157 if not os.path.isfile(if_file):
158 logging.error("Interface ifcfg not found: {}".format(if_file))
159 raise FileNotFoundError("Interface file missing: {}".format(if_file))
160 ifcfg_params = generate_ifcfg_params(if_file, network)
161 shutil.move(if_file, "{}.orig".format(if_file))
162 if_content = """DEVICE={}
170 PROMISC=yes""".format(interface, bridge)
172 bridge_content = """DEVICE={}
177 PROMISC=yes""".format(bridge)
178 for param, value in ifcfg_params.items():
180 bridge_content += "\n{}={}".format(param, value)
182 logging.debug("New interface file content:\n{}".format(if_content))
183 logging.debug("New bridge file content:\n{}".format(bridge_content))
184 with open(if_file, 'w') as fh:
186 with open(ovs_file, 'w') as fh:
187 fh.write(bridge_content)
188 logging.info("New network ifcfg files written")
189 logging.info("Restarting Linux networking")
191 subprocess.check_call(['systemctl', 'restart', 'network'])
192 except subprocess.CalledProcessError:
193 logging.error("Failed to restart Linux networking")
197 def detach_interface_from_ovs(network):
199 Detach interface from OVS for baremetal deployments
200 :param network: Apex network to detach single interface from
204 bridge = NET_MAP[network]
205 logging.debug("Detaching interfaces from bridge on network: {}".format(
207 # ensure bridge exists
208 if not is_ovs_bridge(bridge):
211 # check if real port is on bridge
212 for interface in dump_ovs_ports(bridge):
213 if interface and not interface.startswith('vnet'):
214 logging.debug("Interface found: {}".format(interface))
215 real_interface = interface
218 logging.info("No jumphost interface exists on bridge {}".format(
222 # check if original backup ifcfg file exists or create
223 orig_ifcfg_file = os.path.join(NET_CFG_PATH,
224 "ifcfg-{}.orig".format(real_interface))
225 ifcfg_file = orig_ifcfg_file[:-len('.orig')]
226 bridge_ifcfg_file = os.path.join(NET_CFG_PATH,
227 "ifcfg-{}".format(bridge))
228 if os.path.isfile(orig_ifcfg_file):
229 logging.debug("Original interface file found: "
230 "{}".format(orig_ifcfg_file))
232 logging.info("No original ifcfg file found...will attempt to use "
233 "bridge ifcfg file and re-create")
234 if os.path.isfile(bridge_ifcfg_file):
235 ifcfg_params = generate_ifcfg_params(bridge_ifcfg_file, network)
236 if_content = """DEVICE={}
240 NM_CONTROLLED=no""".format(real_interface)
241 for param, value in ifcfg_params.items():
243 if_content += "\n{}={}".format(param, value)
244 logging.debug("Interface file content:\n{}".format(if_content))
245 # write original backup
246 with open(orig_ifcfg_file, 'w') as fh:
248 logging.debug("Original interface file created: "
249 "{}".format(orig_ifcfg_file))
251 logging.error("Unable to find original interface config file: {} "
252 "or bridge config file:{}".format(orig_ifcfg_file,
254 raise FileNotFoundError("Unable to locate bridge or original "
255 "interface ifcfg file")
257 # move original file back and rewrite bridge ifcfg
258 shutil.move(orig_ifcfg_file, ifcfg_file)
259 bridge_content = """DEVICE={}
264 PROMISC=yes""".format(bridge)
265 with open(bridge_ifcfg_file, 'w') as fh:
266 fh.write(bridge_content)
267 # restart linux networking
268 logging.info("Restarting Linux networking")
270 subprocess.check_call(['systemctl', 'restart', 'network'])
271 except subprocess.CalledProcessError:
272 logging.error("Failed to restart Linux networking")
276 def remove_ovs_bridge(network):
278 Unconfigure and remove an OVS bridge
279 :param network: Apex network to remove OVS bridge for
282 bridge = NET_MAP[network]
283 if is_ovs_bridge(bridge):
284 logging.info("Removing bridge: {}".format(bridge))
286 subprocess.check_call(['ovs-vsctl', 'del-br', bridge])
287 except subprocess.CalledProcessError:
288 logging.error('Unable to destroy OVS bridge')
291 logging.debug('Bridge destroyed')
292 bridge_ifcfg_file = os.path.join(NET_CFG_PATH,
293 "ifcfg-{}".format(bridge))
294 if os.path.isfile(bridge_ifcfg_file):
295 os.remove(bridge_ifcfg_file)
296 logging.debug("Bridge ifcfg file removed: {}".format(
299 logging.debug('Bridge ifcfg file not found')