Permalink
Switch branches/tags
Nothing to show
Find file
Fetching contributors…
Cannot retrieve contributors at this time
468 lines (355 sloc) 15.4 KB
import os
import json
import re
import sys
import subprocess
import time
import urllib.request as urllib2
import multiprocessing as mp
from charmhelpers.core import host
from charmhelpers.core.hookenv import (
open_port,
open_ports,
status_set,
config,
unit_public_ip,
unit_private_ip,
)
from charmhelpers.core.host import (
service_start,
service_stop,
log,
mkdir,
write_file,
)
from charmhelpers.fetch import (
apt_install,
apt_update,
apt_upgrade
)
from charms.reactive.helpers import (
mark_invoked,
was_invoked,
)
from charms.reactive import (
when,
when_not,
when_file_changed,
hook,
RelationBase,
scopes,
set_state,
remove_state
)
CONF_FILE = '/tmp';
#########################################################################
# Common functions
#########################################################################
def run_command(command=None):
if command is None:
return False;
log('Running Command "%s"' % command);
try:
return subprocess.check_output(command, shell=True, stderr=subprocess.STDOUT).decode('utf-8').replace('\n', '');
except subprocess.CalledProcessError as e:
log('Error running "%s" : %s' % (command, e.output));
return False;
def get_config(key):
conf = config(key);
return conf;
def retrieve(key):
try:
conf = open('/tmp/ovn_conf', 'r');
except:
return '';
plain_text = conf.read();
conf.close();
if plain_text == '':
return '';
else:
data = json.loads(plain_text);
return data[key];
def store(key, value):
conf = open('/tmp/ovn_conf', 'r');
plain_text = conf.read();
conf.close();
conf = open('/tmp/ovn_conf', 'w+');
data = {};
if plain_text != '':
data = json.loads(plain_text);
data[key] = value;
conf.truncate(0);
conf.seek(0, 0);
conf.write(json.dumps(data));
conf.close();
#########################################################################
# Hooks and reactive handlers
#########################################################################
''' Common reactive handlers '''
@when_not('deps.installed')
def install_deps():
status_set('maintenance', 'Installing dependencies for OVN');
conf = open('/tmp/ovn_conf', 'w+');
conf.close();
run_command('git clone https://github.com/openvswitch/ovn-kubernetes /tmp/ovn-kubernetes');
os.chdir('/tmp/ovn-kubernetes');
run_command('sudo -H pip2 install .');
set_state('deps.installed');
''' Master reactive handlers and functions '''
def get_worker_subnet():
ip3 = retrieve('ip3');
ip3_int = int(ip3);
new_ip3 = ip3_int + 1;
store('ip3', new_ip3);
return ip3;
@when('master.initialised')
def restart_services():
new_interface = retrieve('new_interface');
old_interface = retrieve('old_interface');
run_command('sudo ovn-k8s-watcher --overlay --pidfile \
--log-file -vfile:info -vconsole:emer --detach');
run_command('sudo ovn-k8s-gateway-helper --physical-bridge=%s \
--physical-interface=%s \
--pidfile --detach' % (new_interface, old_interface));
@when('master.initialised', 'master-config.worker.cert.available')
def sign_and_send(mconfig):
data = mconfig.get_worker_data();
central_ip = get_my_ip();
master_hostname = run_command('hostname');
signed_certs = {};
for unit in data:
worker_hostname = unit['worker_hostname'];
if not was_invoked(worker_hostname):
mark_invoked(worker_hostname);
cert = unit['cert_to_sign'];
ip3 = get_worker_subnet();
worker_subnet = '192.168.%s.0/24' % (ip3);
os.chdir('/tmp/');
cert_file = open('/tmp/ovncontroller-req.pem', 'w+');
cert_file.truncate(0);
cert_file.seek(0, 0);
cert_file.write(cert);
cert_file.close();
run_command('sudo ovs-pki -d /certs/pki -b sign ovncontroller switch --force');
cert_file = open('ovncontroller-cert.pem', 'r');
signed_cert = cert_file.read();
signed_certs[worker_hostname] = {
"central_ip": central_ip,
"signed_cert": signed_cert,
"master_hostname": master_hostname,
"worker_hostname": worker_hostname,
"worker_subnet": worker_subnet,
};
mconfig.send_signed_certs(signed_certs);
@when('cni.is-master', 'master.initialised')
@when_not('gateway.installed')
def install_gateway(cni):
status_set('maintenance', 'Initialising gateway');
run_command('sudo ovs-vsctl set Open_vSwitch . external_ids:k8s-api-server="0.0.0.0:8080"');
run_command('git clone https://github.com/openvswitch/ovn-kubernetes /tmp/ovn-kubernetes');
os.chdir('/tmp/ovn-kubernetes');
run_command('sudo pip2 install .');
old_interface = get_interface(old=True);
new_interface = get_interface(old=False);
op = run_command('ifconfig %s | grep "inet addr:"' % (new_interface));
br_ip = op.lstrip().split()[1].replace('addr:', '');
gateway_ip = run_command('ip route | grep default').split(' ')[2];
hostname = run_command('hostname');
op = run_command('ovn-k8s-overlay gateway-init \
--cluster-ip-subnet="192.168.0.0/16" \
--bridge-interface %s \
--physical-ip %s/32 \
--node-name="%s-gateway" \
--default-gw %s' % (new_interface, br_ip, hostname, gateway_ip));
log('Gateway init output: %s' % (op));
op = run_command('ovn-k8s-gateway-helper --physical-bridge=%s \
--physical-interface=%s --pidfile --detach' % (new_interface, old_interface));
log('Gateway Helper start: %s' % (op));
status_set('active', 'Master subnet : 192.168.1.0/24');
set_state('gateway.installed');
@when('cni.is-master', 'master.setup.done')
@when_not('master.initialised')
def initialise_master(cni):
status_set('maintenance', 'Initialising master network');
central_ip = get_my_ip();
hostname = run_command('hostname');
run_command('sudo ovs-vsctl set Open_vSwitch . external_ids:k8s-api-server="0.0.0.0:8080"');
run_command('sudo ovn-k8s-overlay master-init --cluster-ip-subnet="192.168.0.0/16" \
--master-switch-subnet="192.168.1.0/24" \
--node-name="%s"' % (hostname));
run_command('sudo ovn-k8s-watcher --overlay --pidfile --log-file -vfile:info \
-vconsole:emer --detach');
status_set('maintenance', 'Waiting for gateway');
set_state('master.initialised');
@when('cni.is-master', 'bridge.setup.done')
@when_not('master.setup.done')
def master_setup(cni):
status_set('maintenance', 'Setting up master');
open_ports(1, 2378);
open_ports(2380, 6442);
open_ports(6444, 65535);
central_ip = get_my_ip();
run_command('sudo /usr/share/openvswitch/scripts/ovn-ctl start_northd');
run_command('sudo ovn-nbctl set-connection pssl:6641');
run_command('sudo ovn-sbctl set-connection pssl:6642');
os.chdir('/etc/openvswitch');
run_command('sudo ovs-pki -d /certs/pki init --force');
run_command('sudo cp /certs/pki/switchca/cacert.pem /etc/openvswitch/');
run_command('sudo ovs-pki req ovnnb --force && sudo ovs-pki self-sign ovnnb --force');
run_command('sudo ovn-nbctl set-ssl /etc/openvswitch/ovnnb-privkey.pem \
/etc/openvswitch/ovnnb-cert.pem /certs/pki/switchca/cacert.pem');
run_command('sudo ovs-pki req ovnsb --force && sudo ovs-pki self-sign ovnsb --force');
run_command('sudo ovn-sbctl set-ssl /etc/openvswitch/ovnsb-privkey.pem \
/etc/openvswitch/ovnsb-cert.pem /certs/pki/switchca/cacert.pem');
run_command('sudo ovs-pki req ovncontroller');
run_command('sudo ovs-pki -b -d /certs/pki sign ovncontroller switch --force');
ovn_host_file = open('/etc/default/ovn-host', 'a');
ovn_host_file.write('OVN_CTL_OPTS="--ovn-controller-ssl-key=/etc/openvswitch/ovncontroller-privkey.pem \
--ovn-controller-ssl-cert=/etc/openvswitch/ovncontroller-cert.pem \
--ovn-controller-ssl-bootstrap-ca-cert=/etc/openvswitch/ovnsb-ca.cert"');
ovn_host_file.close();
run_command('sudo ovs-vsctl set Open_vSwitch . external_ids:ovn-remote="ssl:%s:6642" \
external_ids:ovn-nb="ssl:%s:6641" \
external_ids:ovn-encap-ip=%s \
external_ids:ovn-encap-type="%s"' % (central_ip, central_ip, central_ip, 'geneve'));
run_command('sudo /usr/share/openvswitch/scripts/ovn-ctl \
--ovn-controller-ssl-key="/etc/openvswitch/ovncontroller-privkey.pem" \
--ovn-controller-ssl-cert="/etc/openvswitch/ovncontroller-cert.pem" \
--ovn-controller-ssl-bootstrap-ca-cert="/etc/openvswitch/ovnsb-ca.cert" \
restart_controller');
set_state('master.setup.done');
@when('cni.is-master', 'master.kv.setup')
@when_not('bridge.setup.done')
def bridge_setup(cni):
status_set('maintenance', 'Setting up new interface');
interface = get_config('gateway-physical-interface');
if interface == 'none' or interface == None:
op = run_command('ip route | grep default').split(' ');
interface = op[4];
store('old_interface', interface);
store('new_interface', 'br%s' % (interface));
op = run_command('ovn-k8s-util nics-to-bridge %s' % (interface));
log('Bridge create output: %s' % (op));
op = run_command('dhclient -r br%s' % (interface));
op = run_command('dhclient br%s' % (interface));
status_set('maintenance', 'Waiting to initialise master');
set_state('bridge.setup.done');
@when('cni.is-master', 'deps.installed')
@when_not('master.kv.setup')
def setup_master_kv(cni):
store('ip3', '2');
set_state('master.kv.setup');
''' Worker reactive handlers and functions '''
@when('cni.is-worker', 'worker.data.registered')
@when_not('k8s.worker.certs.setup')
def setup_k8s_worker_certs(cni):
if os.path.isfile('/root/cdk/kubeconfig') and os.path.isfile('/root/cdk/ca.crt'):
set_state('k8s.worker.certs.setup');
master_hostname = retrieve('master_hostname');
k8s_api_ip = "%s:6443" % (master_hostname);
api_token = run_command('sudo awk \'$1=="token:" {print $2}\' /root/cdk/kubeconfig');
run_command('sudo cp /root/cdk/ca.crt /etc/openvswitch/k8s-ca.crt');
run_command('sudo ovs-vsctl set Open_vSwitch . \
external_ids:k8s-api-server="https://%s" \
external_ids:k8s-api-token="%s"' % (k8s_api_ip, api_token));
@when('cni.is-worker', 'worker.setup.done')
@when_not('worker.initialised')
def initialise_worker(cni):
status_set('maintenance', 'Initialising worker network');
local_ip = get_my_ip();
worker_subnet = retrieve('worker_subnet');
central_ip = retrieve('central_ip');
hostname = run_command('hostname').replace('\n', '');
run_command('ovs-vsctl set Open_vSwitch . \
external_ids:k8s-api-server="%s:8080"' % (central_ip));
run_command('ovn-k8s-overlay minion-init --cluster-ip-subnet="192.168.0.0/16" \
--minion-switch-subnet="%s" --node-name="%s"' % (worker_subnet, hostname));
os.chdir('/tmp/');
run_command('wget https://github.com/containernetworking/cni/releases/download/v0.5.2/cni-amd64-v0.5.2.tgz');
run_command('sudo mkdir -p /opt/cni/bin');
run_command('sudo mkdir -p /etc/cni/net.d');
os.chdir('/opt/cni/bin/');
run_command('sudo tar xvzf /tmp/cni-amd64-v0.5.2.tgz');
status_set('active', 'Worker subnet : %s' % (worker_subnet));
set_state('worker.initialised');
@when('cni.is-worker', 'worker.data.registered')
@when_not('worker.setup.done')
def worker_setup(cni):
status_set('maintenance', 'Setting up worker');
open_ports(1, 79);
open_ports(81, 442);
open_ports(444, 65535);
central_ip = retrieve('central_ip');
local_ip = get_my_ip();
run_command('sudo ovs-vsctl set Open_vSwitch . \
external_ids:ovn-remote="ssl:%s:6642" \
external_ids:ovn-nb="ssl:%s:6641" \
external_ids:ovn-encap-ip=%s \
external_ids:ovn-encap-type=%s' % (central_ip, central_ip, local_ip, 'geneve'));
ovn_host_file = open('/etc/default/ovn-host', 'a');
ovn_host_file.write('OVN_CTL_OPTS="--ovn-controller-ssl-key=/etc/openvswitch/ovncontroller-privkey.pem \
--ovn-controller-ssl-cert=/etc/openvswitch/ovncontroller-cert.pem \
--ovn-controller-ssl-bootstrap-ca-cert=/etc/openvswitch/ovnsb-ca.cert"');
ovn_host_file.close();
run_command('sudo /usr/share/openvswitch/scripts/ovn-ctl \
--ovn-controller-ssl-key="/etc/openvswitch/ovncontroller-privkey.pem" \
--ovn-controller-ssl-cert="/etc/openvswitch/ovncontroller-cert.pem" \
--ovn-controller-ssl-bootstrap-ca-cert="/etc/openvswitch/ovnsb-ca.cert" \
restart_controller');
set_state('worker.setup.done');
@when('cni.is-worker', 'master-config.master.data.available', 'worker.cert.sent')
@when_not('worker.data.registered')
def receive_data(cni, mconfig):
status_set('maintenance', 'Certificate received')
worker_hostname = run_command('hostname');
data = mconfig.get_signed_cert(worker_hostname);
cert = data['signed_cert'];
worker_subnet = data['worker_subnet'];
master_ip = data['central_ip'];
master_hostname = data['master_hostname'];
store('master_hostname', master_hostname);
store('worker_subnet', worker_subnet);
store('central_ip', master_ip);
cni.set_config(cidr='192.168.0.0/16');
os.chdir('/etc/openvswitch');
cert_file = open('/etc/openvswitch/ovncontroller-cert.pem', 'a');
cert_file.write(cert);
cert_file.close();
set_state('worker.data.registered');
@when('cni.is-worker', 'master-config.connected', 'worker.kv.setup')
@when_not('worker.cert.sent')
def send_cert(cni, mconfig):
worker_hostname = run_command('hostname');
mconfig.set_worker_id(worker_hostname);
os.chdir('/etc/openvswitch');
run_command('sudo ovs-pki req ovncontroller');
req_file = open('ovncontroller-req.pem', 'r');
cert = req_file.read();
mconfig.send_worker_data({
'cert_to_sign': cert,
'worker_hostname': worker_hostname
});
status_set('maintenance', 'Waiting for certificate');
set_state('worker.cert.sent');
@when('cni.is-worker', 'deps.installed')
@when_not('worker.kv.setup')
def setup_worker_kv(cni):
hostname = run_command('hostname');
interface = get_config('gateway-physical-interface');
if interface == 'none' or interface == None:
op = run_command('ip route | grep default').split(' ');
interface = op[4];
store('new_interface', interface);
store('worker_hostname', hostname);
set_state('worker.kv.setup');
#########################################################################
# Helper functions
#########################################################################
def get_my_ip():
interface = get_interface(old=False);
op = run_command('ifconfig %s | grep "inet addr:"' % (interface));
br_ip = op.lstrip().split()[1].replace('addr:', '');
return br_ip;
def get_interface(old):
key = 'old_interface' if old == True else 'new_interface';
return retrieve(key);