Support handling routers

This commit is contained in:
Joshua Boniface 2018-09-24 15:10:12 -04:00
parent 40f36271cc
commit 744e9cab4d
3 changed files with 306 additions and 0 deletions

View File

@ -25,6 +25,7 @@ import click
import client_lib.common as pvc_common import client_lib.common as pvc_common
import client_lib.node as pvc_node import client_lib.node as pvc_node
import client_lib.router as pvc_router
import client_lib.vm as pvc_vm import client_lib.vm as pvc_vm
import client_lib.network as pvc_network import client_lib.network as pvc_network
@ -140,6 +141,85 @@ def node_list(limit):
retcode, retmsg = pvc_node.get_list(zk_conn, limit) retcode, retmsg = pvc_node.get_list(zk_conn, limit)
cleanup(retcode, retmsg, zk_conn) cleanup(retcode, retmsg, zk_conn)
###############################################################################
# pvc router
###############################################################################
@click.group(name='router', short_help='Manage a PVC router.', context_settings=CONTEXT_SETTINGS)
def cli_router():
"""
Manage the state of a router in the PVC cluster.
"""
pass
###############################################################################
# pvc router secondary
###############################################################################
@click.command(name='secondary', short_help='Set a router in secondary status.')
@click.argument(
'router'
)
def router_secondary(router):
"""
Take ROUTER out of primary mode handling gateways and into secondary mode.
"""
zk_conn = pvc_common.startZKConnection(zk_host)
retcode, retmsg = pvc_router.secondary_router(zk_conn, router, wait)
cleanup(retcode, retmsg, zk_conn)
###############################################################################
# pvc router primary
###############################################################################
@click.command(name='primary', short_help='Set a router in primary status.')
@click.argument(
'router'
)
def router_primary(router):
"""
Put ROUTER into primary mode handling gateways.
"""
zk_conn = pvc_common.startZKConnection(zk_host)
retcode, retmsg = pvc_router.primary_router(zk_conn, router)
cleanup(retcode, retmsg, zk_conn)
###############################################################################
# pvc router info
###############################################################################
@click.command(name='info', short_help='Show details of a router object.')
@click.argument(
'router'
)
@click.option(
'-l', '--long', 'long_output', is_flag=True, default=False,
help='Display more detailed information.'
)
def router_info(router, long_output):
"""
Show information about router ROUTER.
"""
zk_conn = pvc_common.startZKConnection(zk_host)
retcode, retmsg = pvc_router.get_info(router, long_output)
cleanup(retcode, retmsg, zk_conn)
###############################################################################
# pvc router list
###############################################################################
@click.command(name='list', short_help='List all router objects.')
@click.argument(
'limit', default=None, required=False
)
def router_list(limit):
"""
List all routers in the cluster; optionally only match names matching regex LIMIT.
"""
zk_conn = pvc_common.startZKConnection(zk_host)
retcode, retmsg = pvc_router.get_list(zk_conn, limit)
cleanup(retcode, retmsg, zk_conn)
############################################################################### ###############################################################################
# pvc vm # pvc vm
############################################################################### ###############################################################################
@ -703,6 +783,11 @@ cli_node.add_command(node_unflush)
cli_node.add_command(node_info) cli_node.add_command(node_info)
cli_node.add_command(node_list) cli_node.add_command(node_list)
cli_router.add_command(router_secondary)
cli_router.add_command(router_primary)
cli_router.add_command(router_info)
cli_router.add_command(router_list)
cli_vm.add_command(vm_define) cli_vm.add_command(vm_define)
cli_vm.add_command(vm_modify) cli_vm.add_command(vm_modify)
cli_vm.add_command(vm_undefine) cli_vm.add_command(vm_undefine)
@ -723,6 +808,7 @@ cli_network.add_command(net_info)
cli_network.add_command(net_list) cli_network.add_command(net_list)
cli.add_command(cli_node) cli.add_command(cli_node)
cli.add_command(cli_router)
cli.add_command(cli_vm) cli.add_command(cli_vm)
cli.add_command(cli_network) cli.add_command(cli_network)
cli.add_command(init_cluster) cli.add_command(init_cluster)

View File

@ -173,6 +173,16 @@ def verifyNode(zk_conn, node):
return True return True
except: except:
return False return False
#
# Verify router is valid in cluster
#
def verifyRouter(zk_conn, router):
try:
zk_conn.get('/routers/{}'.format(router))
return True
except:
return False
# #
# Get the list of valid target hypervisors # Get the list of valid target hypervisors

View File

@ -0,0 +1,210 @@
#!/usr/bin/env python3
# router.py - PVC client function library, router management
# Part of the Parallel Virtual Cluster (PVC) system
#
# Copyright (C) 2018 Joshua M. Boniface <joshua@boniface.me>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
#
###############################################################################
import os
import socket
import time
import uuid
import re
import tempfile
import subprocess
import difflib
import colorama
import click
import lxml.objectify
import configparser
import kazoo.client
import client_lib.ansiiprint as ansiiprint
import client_lib.zkhandler as zkhandler
import client_lib.common as common
def getInformationFromRouter(zk_conn, router_name, long_output):
router_daemon_state = zk_conn.get('/routers/{}/daemonstate'.format(router_name))[0].decode('ascii')
router_network_state = zk_conn.get('/routers/{}/networkstate'.format(router_name))[0].decode('ascii')
router_cpu_count = zk_conn.get('/routers/{}/staticdata'.format(router_name))[0].decode('ascii').split()[0]
router_cpu_load = zk_conn.get('/routers/{}/cpuload'.format(router_name))[0].decode('ascii').split()[0]
router_kernel = zk_conn.get('/routers/{}/staticdata'.format(router_name))[0].decode('ascii').split()[1]
router_os = zk_conn.get('/routers/{}/staticdata'.format(router_name))[0].decode('ascii').split()[2]
router_arch = zk_conn.get('/routers/{}/staticdata'.format(router_name))[0].decode('ascii').split()[3]
if router_daemon_state == 'run':
daemon_state_colour = ansiiprint.green()
elif router_daemon_state == 'stop':
daemon_state_colour = ansiiprint.red()
elif router_daemon_state == 'init':
daemon_state_colour = ansiiprint.yellow()
elif router_daemon_state == 'dead':
daemon_state_colour = ansiiprint.red() + ansiiprint.bold()
else:
daemon_state_colour = ansiiprint.blue()
if router_network_state == 'primary':
network_state_colour = ansiiprint.green()
else:
network_state_colour = ansiiprint.blue()
# Format a nice output; do this line-by-line then concat the elements at the end
ainformation = []
ainformation.append('{}Hypervisor Router information:{}'.format(ansiiprint.bold(), ansiiprint.end()))
ainformation.append('')
# Basic information
ainformation.append('{}Name:{} {}'.format(ansiiprint.purple(), ansiiprint.end(), router_name))
ainformation.append('{}Daemon State:{} {}{}{}'.format(ansiiprint.purple(), ansiiprint.end(), daemon_state_colour, router_daemon_state, ansiiprint.end()))
ainformation.append('{}Domain State:{} {}{}{}'.format(ansiiprint.purple(), ansiiprint.end(), network_state_colour, router_network_state, ansiiprint.end()))
ainformation.append('{}CPUs:{} {}'.format(ansiiprint.purple(), ansiiprint.end(), router_cpu_count))
ainformation.append('{}Load:{} {}'.format(ansiiprint.purple(), ansiiprint.end(), router_cpu_load))
if long_output == True:
ainformation.append('')
ainformation.append('{}Architecture:{} {}'.format(ansiiprint.purple(), ansiiprint.end(), router_arch))
ainformation.append('{}Operating System:{} {}'.format(ansiiprint.purple(), ansiiprint.end(), router_os))
ainformation.append('{}Kernel Version:{} {}'.format(ansiiprint.purple(), ansiiprint.end(), router_kernel))
# Join it all together
information = '\n'.join(ainformation)
return information
#
# Direct Functions
#
def secondary_router(zk_conn, router):
# Verify router is valid
if not common.verifyRouter(zk_conn, router):
return False, 'ERROR: No router named "{}" is present in the cluster.'.format(router)
click.echo('Setting router {} in secondary mode.'.format(router))
zkhandler.writedata(zk_conn, { '/routers/{}/networkstate'.format(router): 'secondary' })
return True, ''
def primary_router(zk_conn, router):
# Verify router is valid
if not common.verifyRouter(zk_conn, router):
return False, 'ERROR: No router named "{}" is present in the cluster.'.format(router)
click.echo('Setting router {} in primary mode.'.format(router))
zkhandler.writedata(zk_conn, { '/routers/{}/networkstate'.format(router): 'primary' })
return True, ''
def get_info(zk_conn, router, long_output):
# Verify router is valid
if not common.verifyRouter(zk_conn, router):
return False, 'ERROR: No router named "{}" is present in the cluster.'.format(router)
# Get information about router in a pretty format
information = getInformationFromRouter(zk_conn, router, long_output)
click.echo(information)
return True, ''
def get_list(zk_conn, limit):
# Match our limit
router_list = []
full_router_list = zk_conn.get_children('/routers')
for router in full_router_list:
if limit != None:
try:
if re.match(limit, router) == None:
continue
except Exception as e:
common.stopZKConnection(zk_conn)
return False, 'Regex Error: {}'.format(e)
router_list.append(router)
router_list_output = []
router_daemon_state = {}
router_network_state = {}
router_cpu_count = {}
router_cpu_load = {}
# Gather information for printing
for router_name in router_list:
router_daemon_state[router_name] = zk_conn.get('/routers/{}/daemonstate'.format(router_name))[0].decode('ascii')
router_network_state[router_name] = zk_conn.get('/routers/{}/networkstate'.format(router_name))[0].decode('ascii')
router_cpu_count[router_name] = zk_conn.get('/routers/{}/staticdata'.format(router_name))[0].decode('ascii').split()[0]
router_cpu_load[router_name] = zk_conn.get('/routers/{}/cpuload'.format(router_name))[0].decode('ascii').split()[0]
# Determine optimal column widths
# Dynamic columns: router_name
router_name_length = 0
for router_name in router_list:
# router_name column
_router_name_length = len(router_name) + 1
if _router_name_length > router_name_length:
router_name_length = _router_name_length
# Format the string (header)
router_list_output.append(
'{bold}{router_name: <{router_name_length}} \
State: {daemon_state_colour}{router_daemon_state: <7}{end_colour} {network_state_colour}{router_network_state: <10}{end_colour} \
Resources: {router_cpu_count: <5} {router_cpu_load: <6}{end_bold}'.format(
router_name_length=router_name_length,
bold=ansiiprint.bold(),
end_bold=ansiiprint.end(),
daemon_state_colour='',
network_state_colour='',
end_colour='',
router_name='Name',
router_daemon_state='Daemon',
router_network_state='Network',
router_cpu_count='CPUs',
router_cpu_load='Load'
)
)
# Format the string (elements)
for router_name in router_list:
if router_daemon_state[router_name] == 'run':
daemon_state_colour = ansiiprint.green()
elif router_daemon_state[router_name] == 'stop':
daemon_state_colour = ansiiprint.red()
elif router_daemon_state[router_name] == 'init':
daemon_state_colour = ansiiprint.yellow()
elif router_daemon_state[router_name] == 'dead':
daemon_state_colour = ansiiprint.red() + ansiiprint.bold()
else:
daemon_state_colour = ansiiprint.blue()
if router_network_state[router_name] == 'primary':
network_state_colour = ansiiprint.green()
else:
network_state_colour = ansiiprint.blue()
router_list_output.append(
'{bold}{router_name: <{router_name_length}} \
{daemon_state_colour}{router_daemon_state: <7}{end_colour} {network_state_colour}{router_network_state: <10}{end_colour} \
{router_cpu_count: <5} {router_cpu_load: <6}{end_bold}'.format(
router_name_length=router_name_length,
bold='',
end_bold='',
daemon_state_colour=daemon_state_colour,
network_state_colour=network_state_colour,
end_colour=ansiiprint.end(),
router_name=router_name,
router_daemon_state=router_daemon_state[router_name],
router_network_state=router_network_state[router_name],
router_cpu_count=router_cpu_count[router_name],
router_cpu_load=router_cpu_load[router_name]
)
)
click.echo('\n'.join(sorted(router_list_output)))
return True, ''