Migrates Apex to Python
[apex.git] / apex / network / jumphost.py
1 ##############################################################################
2 # Copyright (c) 2017 Tim Rozet (trozet@redhat.com) and others.
3 #
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 ##############################################################################
9
10 import logging
11 import os
12 import re
13 import shutil
14 import subprocess
15
16 from apex.common.exceptions import ApexDeployException
17 from apex.network import ip_utils
18
19 NET_MAP = {
20     'admin': 'br-admin',
21     'tenant': 'br-tenant',
22     'external': 'br-external',
23     'storage': 'br-storage',
24     'api': 'br-api'
25 }
26
27
28 def configure_bridges(ns):
29     """
30     Configures IP on jumphost bridges
31     :param ns: network_settings
32     :return: None
33     """
34     bridge_networks = ['admin']
35     if 'external' in ns.enabled_network_list:
36         bridge_networks.append('external')
37     for network in bridge_networks:
38         if network == 'external':
39             net_config = ns['networks'][network][0]
40         else:
41             net_config = ns['networks'][network]
42         cidr = net_config['cidr']
43         interface = ip_utils.get_interface(NET_MAP[network], cidr.version)
44
45         if interface:
46             logging.info("Bridge {} already configured with IP: {}".format(
47                 NET_MAP[network], interface.ip))
48         else:
49             logging.info("Will configure IP for {}".format(NET_MAP[network]))
50             ovs_ip = net_config['overcloud_ip_range'][1]
51             if cidr.version == 6:
52                 ipv6_br_path = "/proc/sys/net/ipv6/conf/{}/disable_" \
53                                "ipv6".format(NET_MAP[network])
54                 try:
55                     subprocess.check_call('echo', 0, '>', ipv6_br_path)
56                 except subprocess.CalledProcessError:
57                     logging.error("Unable to enable ipv6 on "
58                                   "bridge {}".format(NET_MAP[network]))
59                     raise
60             try:
61                 ip_prefix = "{}/{}".format(ovs_ip, cidr.prefixlen)
62                 subprocess.check_call(['ip', 'addr', 'add', ip_prefix, 'dev',
63                                       NET_MAP[network]])
64                 subprocess.check_call(['ip', 'link', 'set', 'up', NET_MAP[
65                     network]])
66                 logging.info("IP configured: {} on bridge {}".format(ovs_ip,
67                              NET_MAP[network]))
68             except subprocess.CalledProcessError:
69                 logging.error("Unable to configure IP address on "
70                               "bridge {}".format(NET_MAP[network]))
71
72
73 def attach_interface_to_ovs(bridge, interface, network):
74     """
75     Attaches jumphost interface to OVS for baremetal deployments
76     :param bridge: bridge to attach to
77     :param interface: interface to attach to bridge
78     :param network: Apex network type for these interfaces
79     :return: None
80     """
81
82     net_cfg_path = '/etc/sysconfig/network-scripts'
83     if_file = os.path.join(net_cfg_path, "ifcfg-{}".format(interface))
84     ovs_file = os.path.join(net_cfg_path, "ifcfg-{}".format(bridge))
85
86     logging.info("Attaching interface: {} to bridge: {} on network {}".format(
87         bridge, interface, network
88     ))
89
90     try:
91         output = subprocess.check_output(['ovs-vsctl', 'list-ports', bridge],
92                                          stderr=subprocess.STDOUT)
93         if bridge in output:
94             logging.debug("Interface already attached to bridge")
95             return
96     except subprocess.CalledProcessError as e:
97         logging.error("Unable to dump ports for bridge: {}".format(bridge))
98         logging.error("Error output: {}".format(e.output))
99         raise
100
101     if not os.path.isfile(if_file):
102         logging.error("Interface ifcfg not found: {}".format(if_file))
103         raise FileNotFoundError("Interface file missing: {}".format(if_file))
104
105     ifcfg_params = {
106         'IPADDR': '',
107         'NETMASK': '',
108         'GATEWAY': '',
109         'METRIC': '',
110         'DNS1': '',
111         'DNS2': '',
112         'PREFIX': ''
113     }
114     with open(if_file, 'r') as fh:
115         interface_output = fh.read()
116
117     for param in ifcfg_params.keys():
118         match = re.search("{}=(.*)\n".format(param), interface_output)
119         if match:
120             ifcfg_params[param] = match.group(1)
121
122     if not ifcfg_params['IPADDR']:
123         logging.error("IPADDR missing in {}".format(if_file))
124         raise ApexDeployException("IPADDR missing in {}".format(if_file))
125     if not (ifcfg_params['NETMASK'] or ifcfg_params['PREFIX']):
126         logging.error("NETMASK/PREFIX missing in {}".format(if_file))
127         raise ApexDeployException("NETMASK/PREFIX missing in {}".format(
128             if_file))
129     if network == 'external' and not ifcfg_params['GATEWAY']:
130         logging.error("GATEWAY is required to be in {} for external "
131                       "network".format(if_file))
132         raise ApexDeployException("GATEWAY is required to be in {} for "
133                                   "external network".format(if_file))
134
135     shutil.move(if_file, "{}.orig".format(if_file))
136     if_content = """DEVICE={}
137 DEVICETYPE=ovs
138 TYPE=OVSPort
139 PEERDNS=no
140 BOOTPROTO=static
141 NM_CONTROLLED=no
142 ONBOOT=yes
143 OVS_BRIDGE={}
144 PROMISC=yes""".format(interface, bridge)
145
146     bridge_content = """DEVICE={}
147 DEVICETYPE=ovs
148 BOOTPROTO=static
149 ONBOOT=yes
150 TYPE=OVSBridge
151 PROMISC=yes""".format(bridge)
152     peer_dns = 'no'
153     for param, value in ifcfg_params.items():
154         if value:
155             bridge_content += "\n{}={}".format(param, value)
156             if param == 'DNS1' or param == 'DNS2':
157                 peer_dns = 'yes'
158     bridge_content += "\n{}={}".format('PEERDNS', peer_dns)
159
160     logging.debug("New interface file content:\n{}".format(if_content))
161     logging.debug("New bridge file content:\n{}".format(bridge_content))
162     with open(if_file, 'w') as fh:
163         fh.write(if_content)
164     with open(ovs_file, 'w') as fh:
165         fh.write(bridge_content)
166     logging.info("New network ifcfg files written")
167     logging.info("Restarting Linux networking")
168     try:
169         subprocess.check_call(['systemctl', 'restart', 'network'])
170     except subprocess.CalledProcessError:
171         logging.error("Failed to restart Linux networking")
172         raise