summaryrefslogtreecommitdiffstats
path: root/apex/network/jumphost.py
blob: f3f06ad6c3603a738e39e86ed3488079a9437879 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
##############################################################################
# Copyright (c) 2017 Tim Rozet (trozet@redhat.com) and others.
#
# All rights reserved. This program and the accompanying materials
# are made available under the terms of the Apache License, Version 2.0
# which accompanies this distribution, and is available at
# http://www.apache.org/licenses/LICENSE-2.0
##############################################################################

import logging
import os
import re
import shutil
import subprocess

from apex.common.exceptions import ApexDeployException
from apex.network import ip_utils

NET_MAP = {
    'admin': 'br-admin',
    'tenant': 'br-tenant',
    'external': 'br-external',
    'storage': 'br-storage',
    'api': 'br-api'
}


def configure_bridges(ns):
    """
    Configures IP on jumphost bridges
    :param ns: network_settings
    :return: None
    """
    bridge_networks = ['admin']
    if 'external' in ns.enabled_network_list:
        bridge_networks.append('external')
    for network in bridge_networks:
        if network == 'external':
            net_config = ns['networks'][network][0]
        else:
            net_config = ns['networks'][network]
        cidr = net_config['cidr']
        interface = ip_utils.get_interface(NET_MAP[network], cidr.version)

        if interface:
            logging.info("Bridge {} already configured with IP: {}".format(
                NET_MAP[network], interface.ip))
        else:
            logging.info("Will configure IP for {}".format(NET_MAP[network]))
            ovs_ip = net_config['overcloud_ip_range'][1]
            if cidr.version == 6:
                ipv6_br_path = "/proc/sys/net/ipv6/conf/{}/disable_" \
                               "ipv6".format(NET_MAP[network])
                try:
                    subprocess.check_call('echo', 0, '>', ipv6_br_path)
                except subprocess.CalledProcessError:
                    logging.error("Unable to enable ipv6 on "
                                  "bridge {}".format(NET_MAP[network]))
                    raise
            try:
                ip_prefix = "{}/{}".format(ovs_ip, cidr.prefixlen)
                subprocess.check_call(['ip', 'addr', 'add', ip_prefix, 'dev',
                                      NET_MAP[network]])
                subprocess.check_call(['ip', 'link', 'set', 'up', NET_MAP[
                    network]])
                logging.info("IP configured: {} on bridge {}".format(ovs_ip,
                             NET_MAP[network]))
            except subprocess.CalledProcessError:
                logging.error("Unable to configure IP address on "
                              "bridge {}".format(NET_MAP[network]))


def attach_interface_to_ovs(bridge, interface, network):
    """
    Attaches jumphost interface to OVS for baremetal deployments
    :param bridge: bridge to attach to
    :param interface: interface to attach to bridge
    :param network: Apex network type for these interfaces
    :return: None
    """

    net_cfg_path = '/etc/sysconfig/network-scripts'
    if_file = os.path.join(net_cfg_path, "ifcfg-{}".format(interface))
    ovs_file = os.path.join(net_cfg_path, "ifcfg-{}".format(bridge))

    logging.info("Attaching interface: {} to bridge: {} on network {}".format(
        bridge, interface, network
    ))

    try:
        output = subprocess.check_output(['ovs-vsctl', 'show'],
                                         stderr=subprocess.STDOUT)
        if bridge not in output.decode('utf-8'):
            logging.debug("Bridge {} not found. Creating...".format(bridge))
            subprocess.check_call(['ovs-vsctl', 'add-br', bridge])
        else:
            logging.debug("Bridge {} found".format(bridge))
    except subprocess.CalledProcessError:
        logging.error("Unable to validate/create OVS bridge {}".format(bridge))
        raise
    try:
        output = subprocess.check_output(['ovs-vsctl', 'list-ports', bridge],
                                         stderr=subprocess.STDOUT)
        if interface in output.decode('utf-8'):
            logging.debug("Interface already attached to bridge")
            return
    except subprocess.CalledProcessError as e:
        logging.error("Unable to dump ports for bridge: {}".format(bridge))
        logging.error("Error output: {}".format(e.output))
        raise

    if not os.path.isfile(if_file):
        logging.error("Interface ifcfg not found: {}".format(if_file))
        raise FileNotFoundError("Interface file missing: {}".format(if_file))

    ifcfg_params = {
        'IPADDR': '',
        'NETMASK': '',
        'GATEWAY': '',
        'METRIC': '',
        'DNS1': '',
        'DNS2': '',
        'PREFIX': ''
    }
    with open(if_file, 'r') as fh:
        interface_output = fh.read()

    for param in ifcfg_params.keys():
        match = re.search("{}=(.*)\n".format(param), interface_output)
        if match:
            ifcfg_params[param] = match.group(1)

    if not ifcfg_params['IPADDR']:
        logging.error("IPADDR missing in {}".format(if_file))
        raise ApexDeployException("IPADDR missing in {}".format(if_file))
    if not (ifcfg_params['NETMASK'] or ifcfg_params['PREFIX']):
        logging.error("NETMASK/PREFIX missing in {}".format(if_file))
        raise ApexDeployException("NETMASK/PREFIX missing in {}".format(
            if_file))
    if network == 'external' and not ifcfg_params['GATEWAY']:
        logging.error("GATEWAY is required to be in {} for external "
                      "network".format(if_file))
        raise ApexDeployException("GATEWAY is required to be in {} for "
                                  "external network".format(if_file))

    shutil.move(if_file, "{}.orig".format(if_file))
    if_content = """DEVICE={}
DEVICETYPE=ovs
TYPE=OVSPort
PEERDNS=no
BOOTPROTO=static
NM_CONTROLLED=no
ONBOOT=yes
OVS_BRIDGE={}
PROMISC=yes""".format(interface, bridge)

    bridge_content = """DEVICE={}
DEVICETYPE=ovs
BOOTPROTO=static
ONBOOT=yes
TYPE=OVSBridge
PROMISC=yes""".format(bridge)
    peer_dns = 'no'
    for param, value in ifcfg_params.items():
        if value:
            bridge_content += "\n{}={}".format(param, value)
            if param == 'DNS1' or param == 'DNS2':
                peer_dns = 'yes'
    bridge_content += "\n{}={}".format('PEERDNS', peer_dns)

    logging.debug("New interface file content:\n{}".format(if_content))
    logging.debug("New bridge file content:\n{}".format(bridge_content))
    with open(if_file, 'w') as fh:
        fh.write(if_content)
    with open(ovs_file, 'w') as fh:
        fh.write(bridge_content)
    logging.info("New network ifcfg files written")
    logging.info("Restarting Linux networking")
    try:
        subprocess.check_call(['systemctl', 'restart', 'network'])
    except subprocess.CalledProcessError:
        logging.error("Failed to restart Linux networking")
        raise