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])
57 subprocess.check_call('echo', 0, '>', ipv6_br_path)
58 except subprocess.CalledProcessError:
59 logging.error("Unable to enable ipv6 on "
60 "bridge {}".format(NET_MAP[network]))
63 ip_prefix = "{}/{}".format(ovs_ip, cidr.prefixlen)
64 subprocess.check_call(['ip', 'addr', 'add', ip_prefix, 'dev',
66 subprocess.check_call(['ip', 'link', 'set', 'up', NET_MAP[
68 logging.info("IP configured: {} on bridge {}".format(ovs_ip,
70 except subprocess.CalledProcessError:
71 logging.error("Unable to configure IP address on "
72 "bridge {}".format(NET_MAP[network]))
76 def generate_ifcfg_params(if_file, network):
78 Generates and validates ifcfg parameters required for a network
79 :param if_file: ifcfg file to parse
80 :param network: Apex network
81 :return: dictionary of generated/validated ifcfg params
83 ifcfg_params = parsers.parse_ifcfg_file(if_file)
84 if not ifcfg_params['IPADDR']:
85 logging.error("IPADDR missing in {}".format(if_file))
86 raise JumpHostNetworkException("IPADDR missing in {}".format(if_file))
87 if not (ifcfg_params['NETMASK'] or ifcfg_params['PREFIX']):
88 logging.error("NETMASK/PREFIX missing in {}".format(if_file))
89 raise JumpHostNetworkException("NETMASK/PREFIX missing in {}".format(
91 if network == 'external' and not ifcfg_params['GATEWAY']:
92 logging.error("GATEWAY is required to be in {} for external "
93 "network".format(if_file))
94 raise JumpHostNetworkException("GATEWAY is required to be in {} for "
95 "external network".format(if_file))
97 if ifcfg_params['DNS1'] or ifcfg_params['DNS2']:
98 ifcfg_params['PEERDNS'] = 'yes'
100 ifcfg_params['PEERDNS'] = 'no'
104 def is_ovs_bridge(bridge):
107 :param bridge: OVS bridge to find
108 :return: boolean if OVS bridge exists
111 output = subprocess.check_output(['ovs-vsctl', 'show'],
112 stderr=subprocess.STDOUT)
113 if bridge not in output.decode('utf-8'):
114 logging.debug("Bridge {} not found".format(bridge))
117 logging.debug("Bridge {} found".format(bridge))
119 except subprocess.CalledProcessError:
120 logging.error("Unable to validate OVS bridge {}".format(bridge))
124 def dump_ovs_ports(bridge):
127 :param bridge: OVS bridge to list ports
128 :return: list of ports
131 output = subprocess.check_output(['ovs-vsctl', 'list-ports', bridge],
132 stderr=subprocess.STDOUT)
133 except subprocess.CalledProcessError:
134 logging.error("Unable to show ports for {}".format(bridge))
136 return output.decode('utf-8').strip().split('\n')
139 def attach_interface_to_ovs(bridge, interface, network):
141 Attaches jumphost interface to OVS for baremetal deployments
142 :param bridge: bridge to attach to
143 :param interface: interface to attach to bridge
144 :param network: Apex network type for these interfaces
148 if_file = os.path.join(NET_CFG_PATH, "ifcfg-{}".format(interface))
149 ovs_file = os.path.join(NET_CFG_PATH, "ifcfg-{}".format(bridge))
151 logging.info("Attaching interface: {} to bridge: {} on network {}".format(
152 bridge, interface, network
155 if not is_ovs_bridge(bridge):
156 subprocess.check_call(['ovs-vsctl', 'add-br', bridge])
157 elif interface in dump_ovs_ports(bridge):
158 logging.debug("Interface already attached to bridge")
161 if not os.path.isfile(if_file):
162 logging.error("Interface ifcfg not found: {}".format(if_file))
163 raise FileNotFoundError("Interface file missing: {}".format(if_file))
164 ifcfg_params = generate_ifcfg_params(if_file, network)
165 shutil.move(if_file, "{}.orig".format(if_file))
166 if_content = """DEVICE={}
174 PROMISC=yes""".format(interface, bridge)
176 bridge_content = """DEVICE={}
181 PROMISC=yes""".format(bridge)
182 for param, value in ifcfg_params.items():
184 bridge_content += "\n{}={}".format(param, value)
186 logging.debug("New interface file content:\n{}".format(if_content))
187 logging.debug("New bridge file content:\n{}".format(bridge_content))
188 with open(if_file, 'w') as fh:
190 with open(ovs_file, 'w') as fh:
191 fh.write(bridge_content)
192 logging.info("New network ifcfg files written")
193 logging.info("Restarting Linux networking")
195 subprocess.check_call(['systemctl', 'restart', 'network'])
196 except subprocess.CalledProcessError:
197 logging.error("Failed to restart Linux networking")
201 def detach_interface_from_ovs(network):
203 Detach interface from OVS for baremetal deployments
204 :param network: Apex network to detach single interface from
208 bridge = NET_MAP[network]
209 logging.debug("Detaching interfaces from bridge on network: {}".format(
211 # ensure bridge exists
212 if not is_ovs_bridge(bridge):
215 # check if real port is on bridge
216 for interface in dump_ovs_ports(bridge):
217 if interface and not interface.startswith('vnet'):
218 logging.debug("Interface found: {}".format(interface))
219 real_interface = interface
222 logging.info("No jumphost interface exists on bridge {}".format(
226 # check if original backup ifcfg file exists or create
227 orig_ifcfg_file = os.path.join(NET_CFG_PATH,
228 "ifcfg-{}.orig".format(real_interface))
229 ifcfg_file = orig_ifcfg_file[:-len('.orig')]
230 bridge_ifcfg_file = os.path.join(NET_CFG_PATH,
231 "ifcfg-{}".format(bridge))
232 if os.path.isfile(orig_ifcfg_file):
233 logging.debug("Original interface file found: "
234 "{}".format(orig_ifcfg_file))
236 logging.info("No original ifcfg file found...will attempt to use "
237 "bridge ifcfg file and re-create")
238 if os.path.isfile(bridge_ifcfg_file):
239 ifcfg_params = generate_ifcfg_params(bridge_ifcfg_file, network)
240 if_content = """DEVICE={}
244 NM_CONTROLLED=no""".format(real_interface)
245 for param, value in ifcfg_params.items():
247 if_content += "\n{}={}".format(param, value)
248 logging.debug("Interface file content:\n{}".format(if_content))
249 # write original backup
250 with open(orig_ifcfg_file, 'w') as fh:
252 logging.debug("Original interface file created: "
253 "{}".format(orig_ifcfg_file))
255 logging.error("Unable to find original interface config file: {} "
256 "or bridge config file:{}".format(orig_ifcfg_file,
258 raise FileNotFoundError("Unable to locate bridge or original "
259 "interface ifcfg file")
261 # move original file back and rewrite bridge ifcfg
262 shutil.move(orig_ifcfg_file, ifcfg_file)
263 bridge_content = """DEVICE={}
268 PROMISC=yes""".format(bridge)
269 with open(bridge_ifcfg_file, 'w') as fh:
270 fh.write(bridge_content)
271 # restart linux networking
272 logging.info("Restarting Linux networking")
274 subprocess.check_call(['systemctl', 'restart', 'network'])
275 except subprocess.CalledProcessError:
276 logging.error("Failed to restart Linux networking")
280 def remove_ovs_bridge(network):
282 Unconfigure and remove an OVS bridge
283 :param network: Apex network to remove OVS bridge for
286 bridge = NET_MAP[network]
287 if is_ovs_bridge(bridge):
288 logging.info("Removing bridge: {}".format(bridge))
290 subprocess.check_call(['ovs-vsctl', 'del-br', bridge])
291 except subprocess.CalledProcessError:
292 logging.error('Unable to destroy OVS bridge')
295 logging.debug('Bridge destroyed')
296 bridge_ifcfg_file = os.path.join(NET_CFG_PATH,
297 "ifcfg-{}".format(bridge))
298 if os.path.isfile(bridge_ifcfg_file):
299 os.remove(bridge_ifcfg_file)
300 logging.debug("Bridge ifcfg file removed: {}".format(
303 logging.debug('Bridge ifcfg file not found')