Philippe Caseiro
78a27b4fdd
Ajouter la possibilité de créer une grappe haute disponibilité entre plusieurs Hâpy (https://docs.opennebula.org/5.6/advanced_components/ha/index.html) Pré-requis : Les datastores doivent être partagés entre tous les Hâpy via NFS ou tout autre système de fichiers accécibles de manière concurrente entre les serveurs (Glusterfs, Ceph, DRBD). Pour faire ce développement nous nous sommes basés sur eole-glusterfs qui permet de créer une grappe glusterfs (https://dev-eole.ac-dijon.fr/projects/eole-glusterfs)
384 lines
13 KiB
Python
Executable File
384 lines
13 KiB
Python
Executable File
#!/usr/bin/env python
|
|
|
|
from pyeole import process
|
|
from pyeole.log import init_logging
|
|
|
|
from creole.client import CreoleClient
|
|
from tempfile import mkstemp
|
|
|
|
import sys
|
|
import os
|
|
import csv
|
|
import logging
|
|
import re
|
|
|
|
LOG_FILE = '/var/log/one/eole-one-node.log'
|
|
|
|
logger = None
|
|
|
|
class RunCmdError(Exception):
|
|
pass
|
|
|
|
|
|
class OneClient():
|
|
|
|
def __init__(self, user):
|
|
self.user = None
|
|
self.auth = None
|
|
self.root = '/var/lib/one'
|
|
|
|
if user:
|
|
self.user = user
|
|
else:
|
|
self.user = 'oneadmin'
|
|
|
|
user_info = process.system_out(['getent', 'passwd', user])[1]
|
|
if user_info:
|
|
self.root = user_info.split(':')[5]
|
|
|
|
command = ['cat', u'{0}/.one/one_auth'.format(self.root)]
|
|
res = process.system_out(command)
|
|
if res[0] == 0:
|
|
self.auth = res[1].split(':')
|
|
else:
|
|
msg = u"OneClient: unable to authenticate: '{stdout}', '{stderr}'"
|
|
logger.error(msg.format(stdout=res[1], stderr=res[2]))
|
|
|
|
|
|
def __run_cmd__(self, cmd):
|
|
logger.debug(u"OneClient: execute command '{cmd}'".format(cmd=cmd))
|
|
|
|
command = list(cmd)
|
|
command.extend(['--user', self.auth[0]])
|
|
command.extend(['--password', self.auth[1]])
|
|
|
|
res = process.system_out(command)
|
|
if res[0] == 0:
|
|
if 'list' in cmd:
|
|
out_lines = []
|
|
if res:
|
|
for line in res[1].split('\n'):
|
|
if len(line) == 0:
|
|
next
|
|
elif re.match('^.*ID ', line):
|
|
next
|
|
else:
|
|
out_lines.append(line.split())
|
|
return out_lines
|
|
else:
|
|
return res
|
|
else:
|
|
msg = u"OneClient: error executing '{cmd}': '{stdout}', '{stderr}'"
|
|
logger.error(msg.format(cmd=cmd, stdout=res[1], stderr=res[2]))
|
|
return False
|
|
|
|
def get_hosts(self):
|
|
""" get the list of hosts
|
|
"""
|
|
logger.debug(u"OneClient: list all hosts")
|
|
cmd = ['onehost', 'list']
|
|
cmd.extend(['-l', 'ID,NAME'])
|
|
res = self.__run_cmd__(cmd)
|
|
return(res)
|
|
|
|
def get_clusters(self):
|
|
""" get the cluster list
|
|
"""
|
|
logger.debug(u"OneClient: list all clusters")
|
|
cmd = ['onecluster', 'list']
|
|
cmd.extend(['-l', 'ID,NAME'])
|
|
return self.__run_cmd__(cmd)
|
|
|
|
def get_networks(self):
|
|
""" get the virtual network list
|
|
"""
|
|
logger.debug(u"OneClient: list all networks")
|
|
cmd = ['onevnet', 'list']
|
|
cmd.extend(['-l', 'ID,NAME'])
|
|
return self.__run_cmd__(cmd)
|
|
|
|
def get_cluster_id_by_name(self, name):
|
|
logger.debug(u"OneClient: get cluster named '{name}'".format(name=name))
|
|
cmd = ['onecluster', 'list']
|
|
cmd.extend(['-f', 'NAME={0}'.format(name)])
|
|
res = self.__run_cmd__(cmd)
|
|
ID = res[0][0]
|
|
return ID
|
|
|
|
def get_vnet_id_by_name(self, name):
|
|
logger.debug(u"OneClient: get network named '{name}'".format(name=name))
|
|
cmd = ['onevnet', 'list']
|
|
cmd.extend(['-f', 'NAME={0}'.format(name)])
|
|
res = self.__run_cmd__(cmd)
|
|
ID = res[0][0]
|
|
return ID
|
|
|
|
def create_network(self, templatefile, cluster, vnet_name):
|
|
""" Create a network
|
|
"""
|
|
logger.info(u"OneClient: create network named '{name}'".format(name=vnet_name))
|
|
cmd = ['onevnet', 'create', templatefile]
|
|
res = self.__run_cmd__(cmd)
|
|
if res and res[0] == 0:
|
|
msg = u"OneClient: attach vnet '{vnet}' to cluster '{cluster}'"
|
|
logger.info(msg.format(vnet=vnet_name, cluster=cluster))
|
|
|
|
clt_id = self.get_cluster_id_by_name(cluster)
|
|
vnet_id = self.get_vnet_id_by_name(vnet_name)
|
|
res = self.__run_cmd__(['onecluster', 'addvnet', clt_id, vnet_id])
|
|
|
|
if not res:
|
|
print("Error attaching {0} vnet to {1} cluster".format(vnet_name, cluster))
|
|
return False
|
|
else:
|
|
os.remove(templatefile)
|
|
return True
|
|
else:
|
|
logger.error(u"Creation of virtual network with template {0} failed".format(templatefile))
|
|
return False
|
|
|
|
|
|
def update_network(self, templatefile, cluster, vnet_name):
|
|
""" Update a network
|
|
"""
|
|
logger.info(u"OneClient: update network named '{name}'".format(name=vnet_name))
|
|
vnet_id = self.get_vnet_id_by_name(vnet_name)
|
|
cmd = ['onevnet', 'update']
|
|
cmd.extend(['--user', self.auth[0]])
|
|
cmd.extend(['--password', self.auth[1][:-1]])
|
|
cmd.extend([vnet_id, templatefile])
|
|
|
|
res = process.system_out(cmd)
|
|
if res[0] == 0:
|
|
os.remove(templatefile)
|
|
return True
|
|
else:
|
|
logger.error(u"Update of virtual network with template {0} failed".format(templatefile))
|
|
return False
|
|
|
|
def delete_network(self, vnet_id):
|
|
logger.info(u"OneClient: delete network with ID '{id}'".format(id=vnet_id))
|
|
cmd = ['onevnet', 'delete']
|
|
cmd.extend(['--user', self.auth[0]])
|
|
cmd.extend(['--password', self.auth[1][:-1]])
|
|
cmd.append(vnet_id)
|
|
|
|
res = process.system_out(cmd)
|
|
if res[0] == 0:
|
|
logger.info(u"Network {0} deleted".format(vnet_id))
|
|
return True
|
|
else:
|
|
logger.error(u"Error deleting network {0}".format(vnet_id))
|
|
return False
|
|
|
|
|
|
class OneNetwork():
|
|
def create(self, one_client):
|
|
logger.debug(u"OneNetwork: create network named '{name}'".format(name=self.zone))
|
|
tmpl_file = self.create_template()
|
|
if one_client.create_network(tmpl_file, self.cluster, self.zone):
|
|
logger.info(u"Virtual network {0} created".format(self.zone))
|
|
return True
|
|
else:
|
|
logger.error(u"Error Creating virtual network {0}".format(self.zone))
|
|
return False
|
|
|
|
def update(self, one_client):
|
|
logger.debug(u"OneNetwork: update network named '{name}'".format(name=self.zone))
|
|
tmpl_file = self.create_template(True)
|
|
if one_client.update_network(tmpl_file, self.cluster, self.zone):
|
|
logger.info(u"Virtual network {0} updated".format(self.zone))
|
|
return True
|
|
else:
|
|
logger.error(u"Error Updating virtual network {0}".format(self.zone))
|
|
return False
|
|
|
|
def manage(self, one_client):
|
|
logger.debug(u"OneNetwork: manage network named '{name}'".format(name=self.zone))
|
|
found = False
|
|
vnet = one_client.get_networks()
|
|
network_name = self.zone
|
|
for net in vnet:
|
|
if network_name in net:
|
|
found = True
|
|
break
|
|
|
|
if not found:
|
|
return self.create(one_client)
|
|
else:
|
|
return self.update(one_client)
|
|
|
|
class OneNetworkL3(OneNetwork):
|
|
def __init__(self, net_info, cluster):
|
|
self.swname = net_info[0]
|
|
self.zone = u'{0}{1}'.format(net_info[10], net_info[1])
|
|
self.vlan = net_info[2]
|
|
self.vnet_addr = net_info[3]
|
|
self.vnet_mask = net_info[4]
|
|
self.vnet_gw = net_info[5]
|
|
self.vnet_rg_start = net_info[6]
|
|
self.vnet_rg_size = net_info[7]
|
|
self.vnet_dns = net_info[8]
|
|
self.vnet_trunk = net_info[9]
|
|
self.cluster = cluster
|
|
|
|
def create_template(self, update=False):
|
|
logger.debug(u"OneNetworkL3: create template for network named '{name}'".format(name=self.zone))
|
|
fd, tmp_path = mkstemp(prefix='oneVnet-')
|
|
template = open(tmp_path, 'w')
|
|
template.write('NAME = "{0}"\n'.format(self.zone))
|
|
template.write('VN_MAD = ovswitch\n')
|
|
|
|
if (update is False):
|
|
if self.vnet_rg_start and self.vnet_rg_size:
|
|
template.write('AR=[\n')
|
|
template.write('TYPE = "IP4",\n')
|
|
template.write('IP = "{0}",\n'.format(self.vnet_rg_start))
|
|
template.write('SIZE = "{0}"\n'.format(self.vnet_rg_size))
|
|
template.write(']\n')
|
|
else:
|
|
template.write('TYPE = FIXED\n')
|
|
|
|
if self.vlan:
|
|
template.write('VLAN = yes\n')
|
|
template.write('VLAN_ID = {0}\n'.format(self.vlan))
|
|
|
|
if self.vnet_trunk:
|
|
template.write('VLAN_TAGGED_ID = {0}\n'.format(self.vnet_trunk))
|
|
|
|
template.write('BRIDGE = {0}\n'.format(self.swname))
|
|
template.write('NETWORK_ADDRESS = {0}\n'.format(self.vnet_addr))
|
|
template.write('NETWORK_MASK = {0}\n'.format(self.vnet_mask))
|
|
template.write('GATEWAY = {0}\n'.format(self.vnet_gw))
|
|
template.write('DNS = {0}\n'.format(self.vnet_dns))
|
|
template.close()
|
|
return tmp_path
|
|
|
|
|
|
class OneNetworkL2(OneNetwork):
|
|
def __init__(self, net_info, cluster):
|
|
self.swname = net_info[0]
|
|
self.zone = u'{0}{1}'.format(net_info[6], net_info[1])
|
|
self.net_size = net_info[2]
|
|
self.first_mac = net_info[3]
|
|
self.tag = net_info[4]
|
|
self.trunk = net_info[5]
|
|
self.cluster = cluster
|
|
|
|
def create_template(self,update=False):
|
|
logger.debug(u"OneNetworkL2: create template for network named '{name}'".format(name=self.zone))
|
|
fd, tmp_path = mkstemp(prefix='oneVnet-')
|
|
template = open(tmp_path, 'w')
|
|
template.write('NAME = "{0}"\n'.format(self.zone))
|
|
template.write('VN_MAD = ovswitch\n')
|
|
|
|
if self.tag:
|
|
template.write('VLAN = yes\n')
|
|
template.write('VLAN_ID = "{0}"\n'.format(self.tag))
|
|
|
|
if self.trunk:
|
|
template.write('VLAN_TAGGED_ID = "{0}""\n'.format(self.trunk))
|
|
|
|
template.write('BRIDGE = {0}\n'.format(self.swname))
|
|
if update is False and self.net_size:
|
|
template.write("AR=[\n")
|
|
template.write(' TYPE = "ETHER",\n')
|
|
|
|
if self.first_mac:
|
|
template.write(' MAC = "{0}",\n'.format(self.first_mac))
|
|
|
|
template.write(' SIZE = "{0}"\n'.format(self.net_size))
|
|
template.write("]\n")
|
|
template.close()
|
|
return tmp_path
|
|
|
|
def main():
|
|
global logger
|
|
logger = init_logging(name='postservice.opennebula.network',
|
|
level=u'INFO',
|
|
as_root=True,
|
|
console=True,
|
|
syslog=True)
|
|
|
|
logger.debug(u"Configure OpenNebula networks")
|
|
|
|
client = CreoleClient()
|
|
|
|
mode_ha = client.get_creole('activer_one_ha')
|
|
if mode_ha == "oui":
|
|
indx = client.get_creole('one_ha_server_index')
|
|
if indx != 0:
|
|
# Mode HA is on and we are not in the Leader
|
|
exit(0)
|
|
|
|
one_client = OneClient('oneadmin')
|
|
networks = []
|
|
cluster = client.get_creole('one_cluster_name')
|
|
swname = client.get_creole('ovs_sw_name')
|
|
zones = client.get_creole('vnets')
|
|
vlans = client.get_creole('vnet_vlan_tag')
|
|
vnet_addr = client.get_creole('vnet_network_addr')
|
|
vnet_mask = client.get_creole('vnet_network_mask')
|
|
vnet_dns = client.get_creole('vnet_network_dns')
|
|
vnet_gw = client.get_creole('vnet_network_gw')
|
|
vnet_rg_start = client.get_creole('vnet_range_start')
|
|
vnet_rg_size = client.get_creole('vnet_range_size')
|
|
vnet_trunk = client.get_creole('vnet_vlan_trunk')
|
|
|
|
l2_vnet = client.get_creole('l2_vnets')
|
|
l2_vnet_size = client.get_creole('l2_vnet_size')
|
|
l2_vnet_vlan_tag = client.get_creole('l2_vnet_vlan_tag')
|
|
l2_vnet_vlan_trunk = client.get_creole('l2_vnet_vlan_trunk')
|
|
l2_vnet_first_mac = client.get_creole('l2_vnet_first_mac')
|
|
|
|
net_prefix = "CR_"
|
|
|
|
processed = []
|
|
for cpt in range(len(zones)):
|
|
if zones[cpt] not in processed:
|
|
info = []
|
|
info.append(swname)
|
|
info.append(zones[cpt])
|
|
info.append(vlans[cpt])
|
|
info.append(vnet_addr[cpt])
|
|
info.append(vnet_mask[cpt])
|
|
info.append(vnet_gw[cpt])
|
|
info.append(vnet_rg_start[cpt])
|
|
info.append(vnet_rg_size[cpt])
|
|
info.append(vnet_dns[cpt])
|
|
info.append(vnet_trunk[cpt])
|
|
info.append(net_prefix)
|
|
networks.append(OneNetworkL3(info, cluster))
|
|
processed.append(zones[cpt])
|
|
|
|
for i in range(len(l2_vnet)):
|
|
if l2_vnet[i] not in processed:
|
|
net_info = []
|
|
net_info.append(swname)
|
|
net_info.append(l2_vnet[i])
|
|
net_info.append(l2_vnet_size[i])
|
|
net_info.append(l2_vnet_first_mac[i])
|
|
net_info.append(l2_vnet_vlan_tag[i])
|
|
net_info.append(l2_vnet_vlan_trunk[i])
|
|
net_info.append(net_prefix)
|
|
networks.append(OneNetworkL2(net_info, cluster))
|
|
processed.append(l2_vnet[i])
|
|
|
|
if client.get_creole('activer_openvswitch'):
|
|
for network in networks:
|
|
if not network.manage(one_client):
|
|
exit(1)
|
|
else:
|
|
logger.info(u'Open vSwitch disabled no need to configure virtual networks')
|
|
|
|
networks = one_client.get_networks()
|
|
for net in networks:
|
|
name = net[1]
|
|
if name.startswith(net_prefix):
|
|
if not name[3:] in zones and not name[3:] in l2_vnet:
|
|
one_client.delete_network(net[0])
|
|
|
|
exit(0)
|
|
|
|
main()
|