2018-09-20 03:25:58 -04:00
#!/usr/bin/env python3
# vm.py - PVC client function library, VM fuctions
# Part of the Parallel Virtual Cluster (PVC) system
#
2019-10-13 12:09:51 -04:00
# Copyright (C) 2018-2019 Joshua M. Boniface <joshua@boniface.me>
2018-09-20 03:25:58 -04:00
#
# 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 subprocess
import difflib
import colorama
import click
import lxml . objectify
import configparser
import kazoo . client
2019-04-11 19:06:06 -04:00
from collections import deque
2018-10-20 15:28:25 -04:00
import client_lib . ansiprint as ansiprint
2018-09-25 02:20:32 -04:00
import client_lib . zkhandler as zkhandler
2018-09-20 03:42:40 -04:00
import client_lib . common as common
2018-09-20 03:25:58 -04:00
2019-06-27 11:19:48 -04:00
import client_lib . ceph as ceph
2018-09-20 03:25:58 -04:00
#
# Cluster search functions
#
def getClusterDomainList ( zk_conn ) :
# Get a list of UUIDs by listing the children of /domains
2018-10-27 15:27:08 -04:00
uuid_list = zkhandler . listchildren ( zk_conn , ' /domains ' )
2018-09-20 03:25:58 -04:00
name_list = [ ]
# For each UUID, get the corresponding name from the data
for uuid in uuid_list :
2018-10-27 15:27:08 -04:00
name_list . append ( zkhandler . readdata ( zk_conn , ' /domains/ %s ' % uuid ) )
2018-09-20 03:25:58 -04:00
return uuid_list , name_list
def searchClusterByUUID ( zk_conn , uuid ) :
try :
# Get the lists
uuid_list , name_list = getClusterDomainList ( zk_conn )
# We're looking for UUID, so find that element ID
index = uuid_list . index ( uuid )
# Get the name_list element at that index
name = name_list [ index ]
except ValueError :
# We didn't find anything
return None
return name
def searchClusterByName ( zk_conn , name ) :
try :
# Get the lists
uuid_list , name_list = getClusterDomainList ( zk_conn )
# We're looking for name, so find that element ID
index = name_list . index ( name )
# Get the uuid_list element at that index
uuid = uuid_list [ index ]
except ValueError :
# We didn't find anything
return None
return uuid
def getDomainUUID ( zk_conn , domain ) :
2019-06-27 11:19:48 -04:00
# Validate that VM exists in cluster
2018-09-20 03:25:58 -04:00
if common . validateUUID ( domain ) :
dom_name = searchClusterByUUID ( zk_conn , domain )
dom_uuid = searchClusterByName ( zk_conn , dom_name )
else :
dom_uuid = searchClusterByName ( zk_conn , domain )
dom_name = searchClusterByUUID ( zk_conn , dom_uuid )
return dom_uuid
2018-09-25 02:26:37 -04:00
def getDomainName ( zk_conn , domain ) :
2019-06-27 11:19:48 -04:00
# Validate that VM exists in cluster
2018-09-25 02:26:37 -04:00
if common . validateUUID ( domain ) :
dom_name = searchClusterByUUID ( zk_conn , domain )
dom_uuid = searchClusterByName ( zk_conn , dom_name )
else :
dom_uuid = searchClusterByName ( zk_conn , domain )
dom_name = searchClusterByUUID ( zk_conn , dom_uuid )
return dom_name
2018-09-20 03:25:58 -04:00
#
# Direct functions
#
2019-07-25 14:33:50 -04:00
def is_migrated ( zk_conn , domain ) :
# Validate that VM exists in cluster
dom_uuid = getDomainUUID ( zk_conn , domain )
if not dom_uuid :
return False , ' ERROR: Could not find VM " {} " in the cluster! ' . format ( domain )
last_node = zkhandler . readdata ( zk_conn , ' /domains/ {} /lastnode ' . format ( dom_uuid ) )
if last_node :
return True
2019-07-25 15:45:45 -04:00
else :
2019-07-25 14:33:50 -04:00
return False
2019-08-07 13:38:49 -04:00
def flush_locks ( zk_conn , domain ) :
# Validate that VM exists in cluster
dom_uuid = getDomainUUID ( zk_conn , domain )
if not dom_uuid :
return False , ' ERROR: Could not find VM " {} " in the cluster! ' . format ( domain )
# Verify that the VM is in a stopped state; freeing locks is not safe otherwise
state = zkhandler . readdata ( zk_conn , ' /domains/ {} /state ' . format ( dom_uuid ) )
if state != ' stop ' :
return False , ' ERROR: VM " {} " is not in stopped state; flushing RBD locks on a running VM is dangerous. ' . format ( domain )
# Tell the cluster to create a new OSD for the host
flush_locks_string = ' flush_locks {} ' . format ( dom_uuid )
zkhandler . writedata ( zk_conn , { ' /cmd/domains ' : flush_locks_string } )
# Wait 1/2 second for the cluster to get the message and start working
time . sleep ( 0.5 )
# Acquire a read lock, so we get the return exclusively
lock = zkhandler . readlock ( zk_conn , ' /cmd/domains ' )
with lock :
try :
result = zkhandler . readdata ( zk_conn , ' /cmd/domains ' ) . split ( ) [ 0 ]
if result == ' success-flush_locks ' :
message = ' Flushed locks on VM " {} " ' . format ( domain )
success = True
else :
message = ' ERROR: Failed to flush locks on VM " {} " ; check node logs for details. ' . format ( domain )
success = False
except :
message = ' ERROR: Command ignored by node. '
success = False
# Acquire a write lock to ensure things go smoothly
lock = zkhandler . writelock ( zk_conn , ' /cmd/domains ' )
with lock :
time . sleep ( 0.5 )
zkhandler . writedata ( zk_conn , { ' /cmd/domains ' : ' ' } )
return success , message
2019-12-11 16:49:11 -05:00
def define_vm ( zk_conn , config_data , target_node , node_limit , node_selector , node_autostart , profile = None ) :
2018-09-20 03:25:58 -04:00
# Parse the XML data
2019-07-09 10:22:23 -04:00
try :
parsed_xml = lxml . objectify . fromstring ( config_data )
except :
return False , ' ERROR: Failed to parse XML data. '
2018-09-20 03:25:58 -04:00
dom_uuid = parsed_xml . uuid . text
dom_name = parsed_xml . name . text
2019-12-09 09:56:59 -05:00
# Ensure that the UUID and name are unique
2019-12-09 11:31:56 -05:00
if searchClusterByUUID ( zk_conn , dom_uuid ) or searchClusterByName ( zk_conn , dom_name ) :
2019-12-09 09:56:59 -05:00
return False , ' ERROR: Specified VM " {} " or UUID " {} " matches an existing VM on the cluster ' . format ( dom_name , dom_uuid )
2019-06-24 13:37:56 -04:00
if not target_node :
2019-10-12 02:03:23 -04:00
target_node = common . findTargetNode ( zk_conn , dom_uuid )
2019-06-24 13:25:24 -04:00
else :
# Verify node is valid
valid_node = common . verifyNode ( zk_conn , target_node )
if not valid_node :
2019-07-09 10:22:23 -04:00
return False , ' ERROR: Specified node " {} " is invalid. ' . format ( target_node )
# Obtain the RBD disk list using the common functions
ddisks = common . getDomainDisks ( parsed_xml )
rbd_list = [ ]
for disk in ddisks :
if disk [ ' type ' ] == ' rbd ' :
rbd_list . append ( disk [ ' name ' ] )
2018-09-20 03:25:58 -04:00
2019-12-19 12:03:46 -05:00
# Join the limit
2019-12-19 13:22:38 -05:00
if isinstance ( node_limit , list ) and node_limit :
2019-12-19 12:03:46 -05:00
formatted_node_limit = ' , ' . join ( node_limit )
else :
2019-12-19 13:22:38 -05:00
formatted_node_limit = ' '
2019-12-19 12:03:46 -05:00
# Join the RBD list
2019-12-19 13:22:38 -05:00
if isinstance ( rbd_list , list ) and rbd_list :
2019-12-19 12:03:46 -05:00
formatted_rbd_list = ' , ' . join ( rbd_list )
else :
2019-12-19 13:22:38 -05:00
formatted_rbd_list = ' '
2019-12-19 12:03:46 -05:00
2018-09-20 03:25:58 -04:00
# Add the new domain to Zookeeper
2018-10-27 15:27:08 -04:00
zkhandler . writedata ( zk_conn , {
' /domains/ {} ' . format ( dom_uuid ) : dom_name ,
' /domains/ {} /state ' . format ( dom_uuid ) : ' stop ' ,
' /domains/ {} /node ' . format ( dom_uuid ) : target_node ,
' /domains/ {} /lastnode ' . format ( dom_uuid ) : ' ' ,
2019-12-19 12:03:46 -05:00
' /domains/ {} /node_limit ' . format ( dom_uuid ) : formatted_node_limit ,
2019-10-12 01:17:39 -04:00
' /domains/ {} /node_selector ' . format ( dom_uuid ) : node_selector ,
' /domains/ {} /node_autostart ' . format ( dom_uuid ) : node_autostart ,
2018-10-27 15:27:08 -04:00
' /domains/ {} /failedreason ' . format ( dom_uuid ) : ' ' ,
2019-04-11 19:06:06 -04:00
' /domains/ {} /consolelog ' . format ( dom_uuid ) : ' ' ,
2019-12-19 12:03:46 -05:00
' /domains/ {} /rbdlist ' . format ( dom_uuid ) : formatted_rbd_list ,
2019-12-11 16:49:11 -05:00
' /domains/ {} /profile ' . format ( dom_uuid ) : profile ,
2018-10-27 15:27:08 -04:00
' /domains/ {} /xml ' . format ( dom_uuid ) : config_data
} )
2018-09-20 03:25:58 -04:00
2019-05-20 22:15:28 -04:00
return True , ' Added new VM with Name " {} " and UUID " {} " to database. ' . format ( dom_name , dom_uuid )
2018-09-20 03:25:58 -04:00
2019-10-12 01:17:39 -04:00
def modify_vm_metadata ( zk_conn , domain , node_limit , node_selector , node_autostart ) :
dom_uuid = getDomainUUID ( zk_conn , domain )
if not dom_uuid :
return False , ' ERROR: Could not find VM " {} " in the cluster! ' . format ( domain )
2019-12-19 13:22:38 -05:00
2019-10-12 01:17:39 -04:00
if node_limit is not None :
2019-12-26 19:08:26 -05:00
zkhandler . writedata ( zk_conn , {
' /domains/ {} /node_limit ' . format ( dom_uuid ) : node_limit
} )
2019-10-12 01:17:39 -04:00
if node_selector is not None :
zkhandler . writedata ( zk_conn , {
' /domains/ {} / node_selector ' . format ( dom_uuid ) : node_selector
} )
if node_autostart is not None :
zkhandler . writedata ( zk_conn , {
' /domains/ {} /node_autostart ' . format ( dom_uuid ) : node_autostart
} )
return True , ' Successfully modified PVC metadata of VM " {} " . ' . format ( domain )
2018-09-25 02:32:08 -04:00
def modify_vm ( zk_conn , domain , restart , new_vm_config ) :
2018-09-20 03:25:58 -04:00
dom_uuid = getDomainUUID ( zk_conn , domain )
2019-04-11 19:06:06 -04:00
if not dom_uuid :
2018-09-20 03:25:58 -04:00
return False , ' ERROR: Could not find VM " {} " in the cluster! ' . format ( domain )
2018-09-25 02:32:08 -04:00
dom_name = getDomainName ( zk_conn , domain )
2018-09-20 03:25:58 -04:00
# Add the modified config to Zookeeper
2018-10-27 15:27:08 -04:00
zk_data = {
' /domains/ {} ' . format ( dom_uuid ) : dom_name ,
' /domains/ {} /xml ' . format ( dom_uuid ) : new_vm_config
}
2018-09-20 03:25:58 -04:00
if restart == True :
2018-10-27 15:27:08 -04:00
zk_data . update ( { ' /domains/ {} /state ' . format ( dom_uuid ) : ' restart ' } )
zkhandler . writedata ( zk_conn , zk_data )
2018-09-20 03:25:58 -04:00
return True , ' '
2019-03-12 21:09:54 -04:00
def dump_vm ( zk_conn , domain ) :
dom_uuid = getDomainUUID ( zk_conn , domain )
2019-04-11 19:06:06 -04:00
if not dom_uuid :
2019-03-12 21:09:54 -04:00
return False , ' ERROR: Could not find VM " {} " in the cluster! ' . format ( domain )
# Gram the domain XML and dump it to stdout
vm_xml = zkhandler . readdata ( zk_conn , ' /domains/ {} /xml ' . format ( dom_uuid ) )
2019-05-20 22:15:28 -04:00
return True , vm_xml
2019-03-12 21:09:54 -04:00
2019-06-27 11:19:48 -04:00
def purge_vm ( zk_conn , domain , is_cli = False ) :
"""
Helper function for both undefine and remove VM to perform the shutdown , termination ,
and configuration deletion .
"""
2019-05-20 22:15:28 -04:00
def undefine_vm ( zk_conn , domain , is_cli = False ) :
2019-06-27 11:19:48 -04:00
# Validate that VM exists in cluster
2018-09-20 03:25:58 -04:00
dom_uuid = getDomainUUID ( zk_conn , domain )
2019-04-11 19:06:06 -04:00
if not dom_uuid :
2018-09-20 03:25:58 -04:00
return False , ' ERROR: Could not find VM " {} " in the cluster! ' . format ( domain )
# Shut down the VM
2019-07-06 01:42:55 -04:00
current_vm_state = zkhandler . readdata ( zk_conn , ' /domains/ {} /state ' . format ( dom_uuid ) )
2019-06-27 11:19:48 -04:00
if current_vm_state != ' stop ' :
if is_cli :
click . echo ( ' Forcibly stopping VM " {} " . ' . format ( domain ) )
# Set the domain into stop mode
zkhandler . writedata ( zk_conn , { ' /domains/ {} /state ' . format ( dom_uuid ) : ' stop ' } )
2018-09-20 03:25:58 -04:00
2019-06-27 11:19:48 -04:00
# Wait for 1 second to allow state to flow to all nodes
2019-05-20 22:15:28 -04:00
if is_cli :
2019-06-27 11:19:48 -04:00
click . echo ( ' Waiting for cluster to update. ' )
2019-05-20 22:15:28 -04:00
time . sleep ( 2 )
2019-06-27 11:19:48 -04:00
# Gracefully terminate the class instances
if is_cli :
click . echo ( ' Deleting VM " {} " from nodes. ' . format ( domain ) )
zkhandler . writedata ( zk_conn , { ' /domains/ {} /state ' . format ( dom_uuid ) : ' delete ' } )
time . sleep ( 2 )
2018-09-20 03:25:58 -04:00
# Delete the configurations
2019-06-27 11:19:48 -04:00
if is_cli :
click . echo ( ' Undefining VM " {} " . ' . format ( domain ) )
zkhandler . deletekey ( zk_conn , ' /domains/ {} ' . format ( dom_uuid ) )
return True , ' Undefined VM " {} " from the cluster. ' . format ( domain )
def remove_vm ( zk_conn , domain , is_cli = False ) :
# Validate that VM exists in cluster
dom_uuid = getDomainUUID ( zk_conn , domain )
if not dom_uuid :
return False , ' ERROR: Could not find VM " {} " in the cluster! ' . format ( domain )
2019-07-09 09:29:47 -04:00
disk_list = common . getDomainDiskList ( zk_conn , dom_uuid )
2019-06-27 11:19:48 -04:00
# Shut down the VM
current_vm_state = zkhandler . readdata ( zk_conn , ' /domains/ {} /state ' . format ( dom_uuid ) )
if current_vm_state != ' stop ' :
2019-05-20 22:15:28 -04:00
if is_cli :
2019-06-27 11:19:48 -04:00
click . echo ( ' Forcibly stopping VM " {} " . ' . format ( domain ) )
# Set the domain into stop mode
zkhandler . writedata ( zk_conn , { ' /domains/ {} /state ' . format ( dom_uuid ) : ' stop ' } )
# Wait for 1 second to allow state to flow to all nodes
if is_cli :
click . echo ( ' Waiting for cluster to update. ' )
time . sleep ( 2 )
# Gracefully terminate the class instances
if is_cli :
click . echo ( ' Deleting VM " {} " from nodes. ' . format ( domain ) )
zkhandler . writedata ( zk_conn , { ' /domains/ {} /state ' . format ( dom_uuid ) : ' delete ' } )
time . sleep ( 2 )
# Delete the configurations
if is_cli :
click . echo ( ' Undefining VM " {} " . ' . format ( domain ) )
zkhandler . deletekey ( zk_conn , ' /domains/ {} ' . format ( dom_uuid ) )
2019-07-10 19:05:36 -04:00
time . sleep ( 2 )
2019-06-27 11:19:48 -04:00
# Remove disks
for disk in disk_list :
# vmpool/vmname_volume
2019-07-31 23:05:00 -04:00
try :
disk_pool , disk_name = disk . split ( ' / ' )
retcode , message = ceph . remove_volume ( zk_conn , disk_pool , disk_name )
if is_cli and message :
click . echo ( ' {} ' . format ( message ) )
except ValueError :
continue
2018-09-20 03:25:58 -04:00
2019-06-27 11:19:48 -04:00
return True , ' Removed VM " {} " and disks from the cluster. ' . format ( domain )
2018-09-20 03:25:58 -04:00
def start_vm ( zk_conn , domain ) :
2019-06-27 11:19:48 -04:00
# Validate that VM exists in cluster
2018-09-20 03:25:58 -04:00
dom_uuid = getDomainUUID ( zk_conn , domain )
2019-04-11 19:06:06 -04:00
if not dom_uuid :
2018-09-20 03:25:58 -04:00
return False , ' ERROR: Could not find VM " {} " in the cluster! ' . format ( domain )
# Set the VM to start
2018-10-27 15:27:08 -04:00
zkhandler . writedata ( zk_conn , { ' /domains/ {} /state ' . format ( dom_uuid ) : ' start ' } )
2018-09-20 03:25:58 -04:00
2019-07-05 16:38:54 -04:00
return True , ' Starting VM " {} " . ' . format ( domain )
2018-09-20 03:25:58 -04:00
def restart_vm ( zk_conn , domain ) :
2019-06-27 11:19:48 -04:00
# Validate that VM exists in cluster
2018-09-20 03:25:58 -04:00
dom_uuid = getDomainUUID ( zk_conn , domain )
2019-04-11 19:06:06 -04:00
if not dom_uuid :
2018-09-20 03:25:58 -04:00
return False , ' ERROR: Could not find VM " {} " in the cluster! ' . format ( domain )
# Get state and verify we're OK to proceed
2018-10-27 15:27:08 -04:00
current_state = zkhandler . readdata ( zk_conn , ' /domains/ {} /state ' . format ( dom_uuid ) )
2018-09-20 03:25:58 -04:00
if current_state != ' start ' :
2019-07-05 16:38:54 -04:00
return False , ' ERROR: VM " {} " is not in " start " state! ' . format ( domain )
2018-09-20 03:25:58 -04:00
# Set the VM to start
2018-10-27 15:27:08 -04:00
zkhandler . writedata ( zk_conn , { ' /domains/ {} /state ' . format ( dom_uuid ) : ' restart ' } )
2018-09-20 03:25:58 -04:00
2019-07-05 16:38:54 -04:00
return True , ' Restarting VM " {} " . ' . format ( domain )
2018-09-20 03:25:58 -04:00
def shutdown_vm ( zk_conn , domain ) :
2019-06-27 11:19:48 -04:00
# Validate that VM exists in cluster
2018-09-20 03:25:58 -04:00
dom_uuid = getDomainUUID ( zk_conn , domain )
2019-04-11 19:06:06 -04:00
if not dom_uuid :
2018-09-20 03:25:58 -04:00
return False , ' ERROR: Could not find VM " {} " in the cluster! ' . format ( domain )
# Get state and verify we're OK to proceed
2018-10-27 15:27:08 -04:00
current_state = zkhandler . readdata ( zk_conn , ' /domains/ {} /state ' . format ( dom_uuid ) )
2018-09-20 03:25:58 -04:00
if current_state != ' start ' :
2019-07-05 16:38:54 -04:00
return False , ' ERROR: VM " {} " is not in " start " state! ' . format ( domain )
2018-09-20 03:25:58 -04:00
# Set the VM to shutdown
2018-10-27 15:27:08 -04:00
zkhandler . writedata ( zk_conn , { ' /domains/ {} /state ' . format ( dom_uuid ) : ' shutdown ' } )
2018-09-20 03:25:58 -04:00
2019-07-05 16:38:54 -04:00
return True , ' Shutting down VM " {} " . ' . format ( domain )
2018-09-20 03:25:58 -04:00
def stop_vm ( zk_conn , domain ) :
2019-06-27 11:19:48 -04:00
# Validate that VM exists in cluster
2018-09-20 03:25:58 -04:00
dom_uuid = getDomainUUID ( zk_conn , domain )
2019-04-11 19:06:06 -04:00
if not dom_uuid :
2018-09-20 03:25:58 -04:00
return False , ' ERROR: Could not find VM " {} " in the cluster! ' . format ( domain )
# Get state and verify we're OK to proceed
2018-10-27 15:27:08 -04:00
current_state = zkhandler . readdata ( zk_conn , ' /domains/ {} /state ' . format ( dom_uuid ) )
2018-09-20 03:25:58 -04:00
# Set the VM to start
2018-10-27 15:27:08 -04:00
zkhandler . writedata ( zk_conn , { ' /domains/ {} /state ' . format ( dom_uuid ) : ' stop ' } )
2018-09-20 03:25:58 -04:00
2019-07-05 16:38:54 -04:00
return True , ' Forcibly stopping VM " {} " . ' . format ( domain )
2018-09-20 03:25:58 -04:00
2019-10-23 23:37:42 -04:00
def disable_vm ( zk_conn , domain ) :
# Validate that VM exists in cluster
dom_uuid = getDomainUUID ( zk_conn , domain )
if not dom_uuid :
return False , ' ERROR: Could not find VM " {} " in the cluster! ' . format ( domain )
# Get state and verify we're OK to proceed
current_state = zkhandler . readdata ( zk_conn , ' /domains/ {} /state ' . format ( dom_uuid ) )
if current_state != ' stop ' :
return False , ' ERROR: VM " {} " must be stopped before disabling! ' . format ( domain )
# Set the VM to start
2019-10-23 23:53:25 -04:00
zkhandler . writedata ( zk_conn , { ' /domains/ {} /state ' . format ( dom_uuid ) : ' disable ' } )
2019-10-23 23:37:42 -04:00
2019-10-23 23:53:25 -04:00
return True , ' Marked VM " {} " as disable. ' . format ( domain )
2019-10-23 23:37:42 -04:00
2019-10-12 01:45:44 -04:00
def move_vm ( zk_conn , domain , target_node ) :
2019-06-27 11:19:48 -04:00
# Validate that VM exists in cluster
2018-09-20 03:25:58 -04:00
dom_uuid = getDomainUUID ( zk_conn , domain )
2019-04-11 19:06:06 -04:00
if not dom_uuid :
2018-09-20 03:25:58 -04:00
return False , ' ERROR: Could not find VM " {} " in the cluster! ' . format ( domain )
2018-10-27 15:27:08 -04:00
current_node = zkhandler . readdata ( zk_conn , ' /domains/ {} /node ' . format ( dom_uuid ) )
2018-09-20 03:25:58 -04:00
2019-06-24 13:37:56 -04:00
if not target_node :
2019-10-12 01:45:44 -04:00
target_node = common . findTargetNode ( zk_conn , dom_uuid )
2018-09-20 03:25:58 -04:00
else :
2019-06-24 13:25:24 -04:00
# Verify node is valid
valid_node = common . verifyNode ( zk_conn , target_node )
if not valid_node :
2019-10-12 01:50:15 -04:00
return False , ' ERROR: Specified node " {} " is invalid. ' . format ( target_node )
2019-06-24 13:25:24 -04:00
2019-10-12 01:45:44 -04:00
# Check if node is within the limit
2019-10-12 01:50:15 -04:00
node_limit = zkhandler . readdata ( zk_conn , ' /domains/ {} /node_limit ' . format ( dom_uuid ) ) . split ( ' , ' )
if node_limit and target_node not in node_limit :
return False , ' ERROR: Specified node " {} " is not in the allowed list of nodes for VM " {} " . ' . format ( target_node , domain )
2019-10-12 01:45:44 -04:00
2019-06-24 13:25:24 -04:00
# Verify if node is current node
2018-10-14 02:01:35 -04:00
if target_node == current_node :
2019-07-05 16:38:54 -04:00
return False , ' ERROR: VM " {} " is already running on node " {} " . ' . format ( domain , current_node )
2018-09-20 03:25:58 -04:00
2019-10-17 12:16:21 -04:00
if not target_node :
return False , ' ERROR: Could not find a valid migration target for VM " {} " . ' . format ( domain )
2018-10-27 15:27:08 -04:00
current_vm_state = zkhandler . readdata ( zk_conn , ' /domains/ {} /state ' . format ( dom_uuid ) )
2018-09-20 03:25:58 -04:00
if current_vm_state == ' start ' :
2018-10-27 15:27:08 -04:00
zkhandler . writedata ( zk_conn , {
' /domains/ {} /state ' . format ( dom_uuid ) : ' migrate ' ,
' /domains/ {} /node ' . format ( dom_uuid ) : target_node ,
' /domains/ {} /lastnode ' . format ( dom_uuid ) : ' '
} )
2018-09-20 03:25:58 -04:00
else :
2018-10-27 15:27:08 -04:00
zkhandler . writedata ( zk_conn , {
' /domains/ {} /node ' . format ( dom_uuid ) : target_node ,
' /domains/ {} /lastnode ' . format ( dom_uuid ) : ' '
} )
2018-09-20 03:25:58 -04:00
2019-07-05 16:38:54 -04:00
return True , ' Permanently migrating VM " {} " to node " {} " . ' . format ( domain , target_node )
2018-09-20 03:25:58 -04:00
2019-10-12 01:45:44 -04:00
def migrate_vm ( zk_conn , domain , target_node , force_migrate , is_cli = False ) :
2019-06-27 11:19:48 -04:00
# Validate that VM exists in cluster
2018-09-20 03:25:58 -04:00
dom_uuid = getDomainUUID ( zk_conn , domain )
2019-04-11 19:06:06 -04:00
if not dom_uuid :
2018-09-20 03:25:58 -04:00
return False , ' ERROR: Could not find VM " {} " in the cluster! ' . format ( domain )
# Get state and verify we're OK to proceed
2018-10-27 15:27:08 -04:00
current_state = zkhandler . readdata ( zk_conn , ' /domains/ {} /state ' . format ( dom_uuid ) )
2018-09-20 03:25:58 -04:00
if current_state != ' start ' :
target_state = ' start '
else :
target_state = ' migrate '
2018-10-27 15:27:08 -04:00
current_node = zkhandler . readdata ( zk_conn , ' /domains/ {} /node ' . format ( dom_uuid ) )
last_node = zkhandler . readdata ( zk_conn , ' /domains/ {} /lastnode ' . format ( dom_uuid ) )
2018-09-20 03:25:58 -04:00
2019-06-24 13:37:56 -04:00
if last_node and not force_migrate :
2019-05-20 22:15:28 -04:00
if is_cli :
2019-07-05 16:38:54 -04:00
click . echo ( ' ERROR: VM " {} " has been previously migrated. ' . format ( domain ) )
2019-05-20 22:15:28 -04:00
click . echo ( ' > Last node: {} ' . format ( last_node ) )
click . echo ( ' > Current node: {} ' . format ( current_node ) )
click . echo ( ' Run `vm unmigrate` to restore the VM to its previous node, or use `--force` to override this check. ' )
return False , ' '
else :
2019-07-05 16:38:54 -04:00
return False , ' ERROR: VM " {} " has been previously migrated. ' . format ( domain )
2018-09-20 03:25:58 -04:00
2019-06-24 13:37:56 -04:00
if not target_node :
2019-10-12 01:45:44 -04:00
target_node = common . findTargetNode ( zk_conn , dom_uuid )
2018-09-20 03:25:58 -04:00
else :
2019-06-24 13:25:24 -04:00
# Verify node is valid
valid_node = common . verifyNode ( zk_conn , target_node )
if not valid_node :
2019-10-12 01:50:15 -04:00
return False , ' ERROR: Specified node " {} " is invalid. ' . format ( target_node )
2019-06-24 13:25:24 -04:00
2019-10-12 01:45:44 -04:00
# Check if node is within the limit
2019-10-12 01:50:15 -04:00
node_limit = zkhandler . readdata ( zk_conn , ' /domains/ {} /node_limit ' . format ( dom_uuid ) ) . split ( ' , ' )
if node_limit and target_node not in node_limit :
return False , ' ERROR: Specified node " {} " is not in the allowed list of nodes for VM " {} " . ' . format ( target_node , domain )
2019-10-12 01:45:44 -04:00
2019-06-24 13:25:24 -04:00
# Verify if node is current node
2018-10-14 02:01:35 -04:00
if target_node == current_node :
2019-07-05 16:38:54 -04:00
return False , ' ERROR: VM " {} " is already running on node " {} " . ' . format ( domain , current_node )
2018-09-20 03:25:58 -04:00
2019-10-17 12:16:21 -04:00
if not target_node :
return False , ' ERROR: Could not find a valid migration target for VM " {} " . ' . format ( domain )
2019-07-07 15:10:48 -04:00
# Don't overwrite an existing last_node when using force_migrate
if last_node and force_migrate :
current_node = last_node
2018-10-27 15:27:08 -04:00
zkhandler . writedata ( zk_conn , {
' /domains/ {} /state ' . format ( dom_uuid ) : ' migrate ' ,
' /domains/ {} /node ' . format ( dom_uuid ) : target_node ,
' /domains/ {} /lastnode ' . format ( dom_uuid ) : current_node
} )
2018-09-20 03:25:58 -04:00
2019-07-05 16:38:54 -04:00
return True , ' Migrating VM " {} " to node " {} " . ' . format ( domain , target_node )
2018-09-20 03:25:58 -04:00
def unmigrate_vm ( zk_conn , domain ) :
2019-06-27 11:19:48 -04:00
# Validate that VM exists in cluster
2018-09-20 03:25:58 -04:00
dom_uuid = getDomainUUID ( zk_conn , domain )
2019-04-11 19:06:06 -04:00
if not dom_uuid :
2018-09-20 03:25:58 -04:00
return False , ' ERROR: Could not find VM " {} " in the cluster! ' . format ( domain )
# Get state and verify we're OK to proceed
2018-10-27 15:27:08 -04:00
current_state = zkhandler . readdata ( zk_conn , ' /domains/ {} /state ' . format ( dom_uuid ) )
2018-09-20 03:25:58 -04:00
if current_state != ' start ' :
2019-03-20 10:19:01 -04:00
# If the current state isn't start, preserve it; we're not doing live migration
target_state = current_state
2018-09-20 03:25:58 -04:00
else :
target_state = ' migrate '
2018-10-27 15:27:08 -04:00
target_node = zkhandler . readdata ( zk_conn , ' /domains/ {} /lastnode ' . format ( dom_uuid ) )
2018-09-20 03:25:58 -04:00
2018-10-14 02:01:35 -04:00
if target_node == ' ' :
2019-07-05 16:38:54 -04:00
return False , ' ERROR: VM " {} " has not been previously migrated. ' . format ( domain )
2018-09-20 03:25:58 -04:00
2018-10-27 15:27:08 -04:00
zkhandler . writedata ( zk_conn , {
' /domains/ {} /state ' . format ( dom_uuid ) : target_state ,
' /domains/ {} /node ' . format ( dom_uuid ) : target_node ,
' /domains/ {} /lastnode ' . format ( dom_uuid ) : ' '
} )
2018-09-20 03:25:58 -04:00
2019-07-05 16:38:54 -04:00
return True , ' Unmigrating VM " {} " back to node " {} " . ' . format ( domain , target_node )
2018-09-20 03:25:58 -04:00
2019-04-11 19:06:06 -04:00
def get_console_log ( zk_conn , domain , lines = 1000 ) :
2019-06-27 11:19:48 -04:00
# Validate that VM exists in cluster
2019-04-11 19:06:06 -04:00
dom_uuid = getDomainUUID ( zk_conn , domain )
if not dom_uuid :
return False , ' ERROR: Could not find VM " {} " in the cluster! ' . format ( domain )
# Get the data from ZK
console_log = zkhandler . readdata ( zk_conn , ' /domains/ {} /consolelog ' . format ( dom_uuid ) )
# Shrink the log buffer to length lines
shrunk_log = console_log . split ( ' \n ' ) [ - lines : ]
loglines = ' \n ' . join ( shrunk_log )
2019-12-25 19:10:12 -05:00
return True , loglines
2019-04-11 19:06:06 -04:00
def follow_console_log ( zk_conn , domain , lines = 10 ) :
2019-06-27 11:19:48 -04:00
# Validate that VM exists in cluster
2019-04-11 19:06:06 -04:00
dom_uuid = getDomainUUID ( zk_conn , domain )
if not dom_uuid :
return False , ' ERROR: Could not find VM " {} " in the cluster! ' . format ( domain )
# Get the initial data from ZK
console_log = zkhandler . readdata ( zk_conn , ' /domains/ {} /consolelog ' . format ( dom_uuid ) )
# Shrink the log buffer to length lines
shrunk_log = console_log . split ( ' \n ' ) [ - lines : ]
loglines = ' \n ' . join ( shrunk_log )
# Print the initial data and begin following
print ( loglines , end = ' ' )
2019-09-07 12:30:31 -04:00
try :
while True :
# Grab the next line set
new_console_log = zkhandler . readdata ( zk_conn , ' /domains/ {} /consolelog ' . format ( dom_uuid ) )
# Split the new and old log strings into constitutent lines
old_console_loglines = console_log . split ( ' \n ' )
new_console_loglines = new_console_log . split ( ' \n ' )
# Set the console log to the new log value for the next iteration
console_log = new_console_log
# Remove the lines from the old log until we hit the first line of the new log; this
# ensures that the old log is a string that we can remove from the new log entirely
for index , line in enumerate ( old_console_loglines , start = 0 ) :
if line == new_console_loglines [ 0 ] :
del old_console_loglines [ 0 : index ]
break
# Rejoin the log lines into strings
old_console_log = ' \n ' . join ( old_console_loglines )
new_console_log = ' \n ' . join ( new_console_loglines )
# Remove the old lines from the new log
diff_console_log = new_console_log . replace ( old_console_log , " " )
# If there's a difference, print it out
if diff_console_log :
print ( diff_console_log , end = ' ' )
# Wait a second
time . sleep ( 1 )
except kazoo . exceptions . NoNodeError :
return False , ' ERROR: VM has gone away. '
except :
return False , ' ERROR: Lost connection to Zookeeper node. '
2019-04-11 19:06:06 -04:00
return True , ' '
2019-05-20 22:15:28 -04:00
def get_info ( zk_conn , domain ) :
2019-06-27 11:19:48 -04:00
# Validate that VM exists in cluster
2019-05-20 22:15:28 -04:00
dom_uuid = getDomainUUID ( zk_conn , domain )
if not dom_uuid :
return False , ' ERROR: No VM named " {} " is present in the cluster. ' . format ( domain )
# Gather information from XML config and print it
2019-07-09 09:29:47 -04:00
domain_information = common . getInformationFromXML ( zk_conn , dom_uuid )
2019-06-24 13:37:56 -04:00
if not domain_information :
2019-05-20 22:15:28 -04:00
return False , ' ERROR: Could not get information about VM " {} " . ' . format ( domain )
return True , domain_information
2019-07-05 16:28:18 -04:00
def get_list ( zk_conn , node , state , limit , is_fuzzy = True ) :
2019-06-24 13:37:56 -04:00
if node :
2018-09-20 03:25:58 -04:00
# Verify node is valid
2019-07-05 14:18:18 -04:00
if not common . verifyNode ( zk_conn , node ) :
2019-07-08 22:37:26 -04:00
return False , ' Specified node " {} " is invalid. ' . format ( node )
2018-09-20 03:25:58 -04:00
2019-06-24 13:37:56 -04:00
if state :
2019-10-23 23:53:25 -04:00
valid_states = [ ' start ' , ' restart ' , ' shutdown ' , ' stop ' , ' disable ' , ' fail ' , ' migrate ' , ' unmigrate ' ]
2019-03-20 11:31:54 -04:00
if not state in valid_states :
return False , ' VM state " {} " is not valid. ' . format ( state )
2018-10-27 15:27:08 -04:00
full_vm_list = zkhandler . listchildren ( zk_conn , ' /domains ' )
2018-09-20 03:25:58 -04:00
vm_list = [ ]
2019-03-12 23:52:59 -04:00
# Set our limit to a sensible regex
2019-07-05 16:28:18 -04:00
if limit and is_fuzzy :
2019-03-12 23:52:59 -04:00
try :
# Implcitly assume fuzzy limits
2019-06-24 13:37:56 -04:00
if not re . match ( ' \ ^.* ' , limit ) :
2019-03-12 23:52:59 -04:00
limit = ' .* ' + limit
2019-06-24 13:37:56 -04:00
if not re . match ( ' .* \ $ ' , limit ) :
2019-03-12 23:52:59 -04:00
limit = limit + ' .* '
except Exception as e :
return False , ' Regex Error: {} ' . format ( e )
2018-09-20 03:25:58 -04:00
# If we're limited, remove other nodes' VMs
2019-05-20 22:15:28 -04:00
vm_node = { }
vm_state = { }
2018-09-25 02:20:32 -04:00
for vm in full_vm_list :
2018-09-20 03:25:58 -04:00
# Check we don't match the limit
2018-09-25 02:20:32 -04:00
name = zkhandler . readdata ( zk_conn , ' /domains/ {} ' . format ( vm ) )
2019-03-12 21:39:17 -04:00
vm_node [ vm ] = zkhandler . readdata ( zk_conn , ' /domains/ {} /node ' . format ( vm ) )
2019-03-20 11:31:54 -04:00
vm_state [ vm ] = zkhandler . readdata ( zk_conn , ' /domains/ {} /state ' . format ( vm ) )
2019-03-12 23:52:59 -04:00
# Handle limiting
2019-06-24 13:37:56 -04:00
if limit :
2018-09-20 03:25:58 -04:00
try :
2019-06-24 13:37:56 -04:00
if re . match ( limit , vm ) :
if not node and not state :
2019-07-09 09:29:47 -04:00
vm_list . append ( common . getInformationFromXML ( zk_conn , vm ) )
2018-09-25 02:20:32 -04:00
else :
2019-03-20 11:31:54 -04:00
if vm_node [ vm ] == node or vm_state [ vm ] == state :
2019-07-09 09:29:47 -04:00
vm_list . append ( common . getInformationFromXML ( zk_conn , vm ) )
2018-09-25 02:20:32 -04:00
2019-06-24 13:37:56 -04:00
if re . match ( limit , name ) :
if not node and not state :
2019-07-09 09:29:47 -04:00
vm_list . append ( common . getInformationFromXML ( zk_conn , vm ) )
2018-09-25 02:20:32 -04:00
else :
2019-03-20 11:31:54 -04:00
if vm_node [ vm ] == node or vm_state [ vm ] == state :
2019-07-09 09:29:47 -04:00
vm_list . append ( common . getInformationFromXML ( zk_conn , vm ) )
2018-09-20 03:25:58 -04:00
except Exception as e :
2018-09-25 02:20:32 -04:00
return False , ' Regex Error: {} ' . format ( e )
2018-09-20 03:25:58 -04:00
else :
2018-10-14 02:01:35 -04:00
# Check node to avoid unneeded ZK calls
2019-06-24 13:37:56 -04:00
if not node and not state :
2019-07-09 09:29:47 -04:00
vm_list . append ( common . getInformationFromXML ( zk_conn , vm ) )
2018-09-25 02:20:32 -04:00
else :
2019-03-20 11:31:54 -04:00
if vm_node [ vm ] == node or vm_state [ vm ] == state :
2019-07-09 09:29:47 -04:00
vm_list . append ( common . getInformationFromXML ( zk_conn , vm ) )
2019-05-20 22:15:28 -04:00
return True , vm_list
#
# CLI-specific functions
#
def format_info ( zk_conn , domain_information , long_output ) :
# Format a nice output; do this line-by-line then concat the elements at the end
ainformation = [ ]
ainformation . append ( ' {} Virtual machine information: {} ' . format ( ansiprint . bold ( ) , ansiprint . end ( ) ) )
ainformation . append ( ' ' )
# Basic information
ainformation . append ( ' {} UUID: {} {} ' . format ( ansiprint . purple ( ) , ansiprint . end ( ) , domain_information [ ' uuid ' ] ) )
ainformation . append ( ' {} Name: {} {} ' . format ( ansiprint . purple ( ) , ansiprint . end ( ) , domain_information [ ' name ' ] ) )
ainformation . append ( ' {} Description: {} {} ' . format ( ansiprint . purple ( ) , ansiprint . end ( ) , domain_information [ ' description ' ] ) )
2019-12-11 16:50:38 -05:00
ainformation . append ( ' {} Profile: {} {} ' . format ( ansiprint . purple ( ) , ansiprint . end ( ) , domain_information [ ' profile ' ] ) )
2019-05-20 22:15:28 -04:00
ainformation . append ( ' {} Memory (M): {} {} ' . format ( ansiprint . purple ( ) , ansiprint . end ( ) , domain_information [ ' memory ' ] ) )
ainformation . append ( ' {} vCPUs: {} {} ' . format ( ansiprint . purple ( ) , ansiprint . end ( ) , domain_information [ ' vcpu ' ] ) )
ainformation . append ( ' {} Topology (S/C/T): {} {} ' . format ( ansiprint . purple ( ) , ansiprint . end ( ) , domain_information [ ' vcpu_topology ' ] ) )
if long_output == True :
# Virtualization information
ainformation . append ( ' ' )
ainformation . append ( ' {} Emulator: {} {} ' . format ( ansiprint . purple ( ) , ansiprint . end ( ) , domain_information [ ' emulator ' ] ) )
ainformation . append ( ' {} Type: {} {} ' . format ( ansiprint . purple ( ) , ansiprint . end ( ) , domain_information [ ' type ' ] ) )
ainformation . append ( ' {} Arch: {} {} ' . format ( ansiprint . purple ( ) , ansiprint . end ( ) , domain_information [ ' arch ' ] ) )
ainformation . append ( ' {} Machine: {} {} ' . format ( ansiprint . purple ( ) , ansiprint . end ( ) , domain_information [ ' machine ' ] ) )
ainformation . append ( ' {} Features: {} {} ' . format ( ansiprint . purple ( ) , ansiprint . end ( ) , ' ' . join ( domain_information [ ' features ' ] ) ) )
2018-09-20 03:25:58 -04:00
2019-05-20 22:15:28 -04:00
# PVC cluster information
ainformation . append ( ' ' )
dstate_colour = {
' start ' : ansiprint . green ( ) ,
' restart ' : ansiprint . yellow ( ) ,
' shutdown ' : ansiprint . yellow ( ) ,
' stop ' : ansiprint . red ( ) ,
2019-10-23 23:53:25 -04:00
' disable ' : ansiprint . blue ( ) ,
' fail ' : ansiprint . red ( ) ,
2019-05-20 22:15:28 -04:00
' migrate ' : ansiprint . blue ( ) ,
' unmigrate ' : ansiprint . blue ( )
}
ainformation . append ( ' {} State: {} {} {} {} ' . format ( ansiprint . purple ( ) , ansiprint . end ( ) , dstate_colour [ domain_information [ ' state ' ] ] , domain_information [ ' state ' ] , ansiprint . end ( ) ) )
ainformation . append ( ' {} Current Node: {} {} ' . format ( ansiprint . purple ( ) , ansiprint . end ( ) , domain_information [ ' node ' ] ) )
2019-12-11 16:50:38 -05:00
if not domain_information [ ' last_node ' ] :
domain_information [ ' last_node ' ] = " N/A "
2019-05-20 22:15:28 -04:00
ainformation . append ( ' {} Previous Node: {} {} ' . format ( ansiprint . purple ( ) , ansiprint . end ( ) , domain_information [ ' last_node ' ] ) )
# Get a failure reason if applicable
2019-06-24 13:37:56 -04:00
if domain_information [ ' failed_reason ' ] :
2019-06-24 09:56:06 -04:00
ainformation . append ( ' ' )
ainformation . append ( ' {} Failure reason: {} {} ' . format ( ansiprint . purple ( ) , ansiprint . end ( ) , domain_information [ ' failed_reason ' ] ) )
2019-05-20 22:15:28 -04:00
2019-12-19 13:29:15 -05:00
if not domain_information [ ' node_selector ' ] :
formatted_node_selector = " False "
else :
formatted_node_selector = domain_information [ ' node_selector ' ]
if not domain_information [ ' node_limit ' ] :
formatted_node_limit = " False "
else :
formatted_node_limit = ' , ' . join ( domain_information [ ' node_limit ' ] )
if not domain_information [ ' node_autostart ' ] :
formatted_node_autostart = " False "
else :
formatted_node_autostart = domain_information [ ' node_autostart ' ]
ainformation . append ( ' {} Migration selector: {} {} ' . format ( ansiprint . purple ( ) , ansiprint . end ( ) , formatted_node_selector ) )
ainformation . append ( ' {} Node limit: {} {} ' . format ( ansiprint . purple ( ) , ansiprint . end ( ) , formatted_node_limit ) )
ainformation . append ( ' {} Autostart: {} {} ' . format ( ansiprint . purple ( ) , ansiprint . end ( ) , formatted_node_autostart ) )
2019-10-12 01:17:39 -04:00
2019-05-20 22:15:28 -04:00
# Network list
net_list = [ ]
for net in domain_information [ ' networks ' ] :
# Split out just the numerical (VNI) part of the brXXXX name
net_vnis = re . findall ( r ' \ d+ ' , net [ ' source ' ] )
if net_vnis :
net_vni = net_vnis [ 0 ]
2018-09-20 03:25:58 -04:00
else :
2019-05-20 22:15:28 -04:00
net_vni = re . sub ( ' br ' , ' ' , net [ ' source ' ] )
net_exists = zkhandler . exists ( zk_conn , ' /networks/ {} ' . format ( net_vni ) )
if not net_exists and net_vni != ' cluster ' :
net_list . append ( ansiprint . red ( ) + net_vni + ansiprint . end ( ) + ' [invalid] ' )
else :
net_list . append ( net_vni )
ainformation . append ( ' ' )
ainformation . append ( ' {} Networks: {} {} ' . format ( ansiprint . purple ( ) , ansiprint . end ( ) , ' , ' . join ( net_list ) ) )
2018-09-20 03:25:58 -04:00
2019-05-20 22:15:28 -04:00
if long_output == True :
# Disk list
ainformation . append ( ' ' )
name_length = 0
for disk in domain_information [ ' disks ' ] :
_name_length = len ( disk [ ' name ' ] ) + 1
if _name_length > name_length :
name_length = _name_length
ainformation . append ( ' {0} Disks: {1} {2} ID Type { 3: < {width} } Dev Bus {4} ' . format ( ansiprint . purple ( ) , ansiprint . end ( ) , ansiprint . bold ( ) , ' Name ' , ansiprint . end ( ) , width = name_length ) )
for disk in domain_information [ ' disks ' ] :
ainformation . append ( ' {0: <3} {1: <5} { 2: < {width} } {3: <4} {4: <5} ' . format ( domain_information [ ' disks ' ] . index ( disk ) , disk [ ' type ' ] , disk [ ' name ' ] , disk [ ' dev ' ] , disk [ ' bus ' ] , width = name_length ) )
ainformation . append ( ' ' )
ainformation . append ( ' {} Interfaces: {} {} ID Type Source Model MAC {} ' . format ( ansiprint . purple ( ) , ansiprint . end ( ) , ansiprint . bold ( ) , ansiprint . end ( ) ) )
2019-06-24 09:56:06 -04:00
for net in domain_information [ ' networks ' ] :
ainformation . append ( ' {0: <3} {1: <8} {2: <10} {3: <8} {4} ' . format ( domain_information [ ' networks ' ] . index ( net ) , net [ ' type ' ] , net [ ' source ' ] , net [ ' model ' ] , net [ ' mac ' ] ) )
2019-05-20 22:15:28 -04:00
# Controller list
ainformation . append ( ' ' )
ainformation . append ( ' {} Controllers: {} {} ID Type Model {} ' . format ( ansiprint . purple ( ) , ansiprint . end ( ) , ansiprint . bold ( ) , ansiprint . end ( ) ) )
for controller in domain_information [ ' controllers ' ] :
ainformation . append ( ' {0: <3} {1: <14} {2: <8} ' . format ( domain_information [ ' controllers ' ] . index ( controller ) , controller [ ' type ' ] , controller [ ' model ' ] ) )
2018-09-20 03:25:58 -04:00
2019-05-20 22:15:28 -04:00
# Join it all together
information = ' \n ' . join ( ainformation )
click . echo ( information )
click . echo ( ' ' )
def format_list ( zk_conn , vm_list , raw ) :
2019-06-24 10:10:07 -04:00
# Function to strip the "br" off of nets and return a nicer list
def getNiceNetID ( domain_information ) :
# Network list
net_list = [ ]
for net in domain_information [ ' networks ' ] :
# Split out just the numerical (VNI) part of the brXXXX name
net_vnis = re . findall ( r ' \ d+ ' , net [ ' source ' ] )
if net_vnis :
net_vni = net_vnis [ 0 ]
else :
net_vni = re . sub ( ' br ' , ' ' , net [ ' source ' ] )
net_list . append ( net_vni )
return net_list
2019-05-20 22:15:28 -04:00
# Handle raw mode since it just lists the names
2019-03-12 21:40:52 -04:00
if raw :
2019-05-21 14:44:45 -04:00
for vm in sorted ( item [ ' name ' ] for item in vm_list ) :
2019-03-12 21:46:09 -04:00
click . echo ( vm )
2019-03-12 21:40:52 -04:00
return True , ' '
2019-05-20 22:15:28 -04:00
vm_list_output = [ ]
2018-09-20 03:25:58 -04:00
# Determine optimal column widths
2018-10-14 02:01:35 -04:00
# Dynamic columns: node_name, node, migrated
2018-11-01 23:24:38 -04:00
vm_name_length = 5
2018-11-02 00:42:44 -04:00
vm_uuid_length = 37
vm_state_length = 6
2018-10-14 02:01:35 -04:00
vm_nets_length = 9
2018-11-02 00:42:44 -04:00
vm_ram_length = 8
vm_vcpu_length = 6
vm_node_length = 8
2018-10-14 02:01:35 -04:00
vm_migrated_length = 10
2019-05-20 22:15:28 -04:00
for domain_information in vm_list :
2019-06-24 10:10:07 -04:00
net_list = getNiceNetID ( domain_information )
2018-09-20 03:25:58 -04:00
# vm_name column
2019-05-20 22:15:28 -04:00
_vm_name_length = len ( domain_information [ ' name ' ] ) + 1
2018-09-20 03:25:58 -04:00
if _vm_name_length > vm_name_length :
vm_name_length = _vm_name_length
2018-11-02 00:42:44 -04:00
# vm_state column
2019-05-20 22:15:28 -04:00
_vm_state_length = len ( domain_information [ ' state ' ] ) + 1
2018-11-02 00:42:44 -04:00
if _vm_state_length > vm_state_length :
vm_state_length = _vm_state_length
2018-10-14 02:01:35 -04:00
# vm_nets column
2019-05-20 22:15:28 -04:00
_vm_nets_length = len ( ' , ' . join ( net_list ) ) + 1
2018-10-14 02:01:35 -04:00
if _vm_nets_length > vm_nets_length :
vm_nets_length = _vm_nets_length
2018-11-02 00:42:44 -04:00
# vm_node column
2019-05-20 22:15:28 -04:00
_vm_node_length = len ( domain_information [ ' node ' ] ) + 1
2018-11-02 00:42:44 -04:00
if _vm_node_length > vm_node_length :
vm_node_length = _vm_node_length
2018-09-20 03:25:58 -04:00
# vm_migrated column
2019-05-20 22:15:28 -04:00
_vm_migrated_length = len ( domain_information [ ' migrated ' ] ) + 1
2018-09-20 03:25:58 -04:00
if _vm_migrated_length > vm_migrated_length :
vm_migrated_length = _vm_migrated_length
# Format the string (header)
vm_list_output . append (
2018-11-02 00:42:44 -04:00
' {bold} { vm_name: < {vm_name_length} } { vm_uuid: < {vm_uuid_length} } \
{ vm_state_colour } { vm_state : < { vm_state_length } } { end_colour } \
2018-10-14 02:01:35 -04:00
{ vm_networks : < { vm_nets_length } } \
2018-11-02 00:42:44 -04:00
{ vm_memory : < { vm_ram_length } } { vm_vcpu : < { vm_vcpu_length } } \
2018-10-14 02:01:35 -04:00
{ vm_node : < { vm_node_length } } \
2018-09-20 03:25:58 -04:00
{ vm_migrated : < { vm_migrated_length } } { end_bold } ' .format(
vm_name_length = vm_name_length ,
2018-11-02 00:42:44 -04:00
vm_uuid_length = vm_uuid_length ,
vm_state_length = vm_state_length ,
2018-10-14 02:01:35 -04:00
vm_nets_length = vm_nets_length ,
2018-11-02 00:42:44 -04:00
vm_ram_length = vm_ram_length ,
vm_vcpu_length = vm_vcpu_length ,
vm_node_length = vm_node_length ,
2018-09-20 03:25:58 -04:00
vm_migrated_length = vm_migrated_length ,
2018-10-20 15:28:25 -04:00
bold = ansiprint . bold ( ) ,
end_bold = ansiprint . end ( ) ,
2018-09-20 03:25:58 -04:00
vm_state_colour = ' ' ,
end_colour = ' ' ,
vm_name = ' Name ' ,
vm_uuid = ' UUID ' ,
vm_state = ' State ' ,
2018-10-14 02:01:35 -04:00
vm_networks = ' Networks ' ,
2018-11-01 23:24:38 -04:00
vm_memory = ' RAM (M) ' ,
2018-09-20 03:25:58 -04:00
vm_vcpu = ' vCPUs ' ,
2018-10-14 02:01:35 -04:00
vm_node = ' Node ' ,
2018-09-20 03:25:58 -04:00
vm_migrated = ' Migrated '
)
)
# Format the string (elements)
2019-05-20 22:15:28 -04:00
for domain_information in vm_list :
if domain_information [ ' state ' ] == ' start ' :
2018-10-20 15:28:25 -04:00
vm_state_colour = ansiprint . green ( )
2019-05-20 22:15:28 -04:00
elif domain_information [ ' state ' ] == ' restart ' :
2018-10-20 15:28:25 -04:00
vm_state_colour = ansiprint . yellow ( )
2019-05-20 22:15:28 -04:00
elif domain_information [ ' state ' ] == ' shutdown ' :
2018-10-20 15:28:25 -04:00
vm_state_colour = ansiprint . yellow ( )
2019-05-20 22:15:28 -04:00
elif domain_information [ ' state ' ] == ' stop ' :
2018-10-20 15:28:25 -04:00
vm_state_colour = ansiprint . red ( )
2019-10-23 23:53:25 -04:00
elif domain_information [ ' state ' ] == ' fail ' :
2018-10-20 15:28:25 -04:00
vm_state_colour = ansiprint . red ( )
2018-09-20 03:25:58 -04:00
else :
2018-10-20 15:28:25 -04:00
vm_state_colour = ansiprint . blue ( )
2018-09-20 03:25:58 -04:00
2018-10-20 15:27:07 -04:00
# Handle colouring for an invalid network config
2019-06-24 10:10:07 -04:00
raw_net_list = getNiceNetID ( domain_information )
2018-10-20 15:27:07 -04:00
net_list = [ ]
2019-07-12 12:22:41 -04:00
vm_net_colour = ' '
2019-06-24 10:10:07 -04:00
for net_vni in raw_net_list :
2019-05-20 22:15:28 -04:00
net_exists = zkhandler . exists ( zk_conn , ' /networks/ {} ' . format ( net_vni ) )
if not net_exists and net_vni != ' cluster ' :
2019-07-12 12:22:41 -04:00
vm_net_colour = ansiprint . red ( )
net_list . append ( net_vni )
2018-10-20 15:27:07 -04:00
2018-09-20 03:25:58 -04:00
vm_list_output . append (
2018-11-02 00:42:44 -04:00
' {bold} { vm_name: < {vm_name_length} } { vm_uuid: < {vm_uuid_length} } \
{ vm_state_colour } { vm_state : < { vm_state_length } } { end_colour } \
2019-07-12 12:22:41 -04:00
{ vm_net_colour } { vm_networks : < { vm_nets_length } } { end_colour } \
2018-11-02 00:42:44 -04:00
{ vm_memory : < { vm_ram_length } } { vm_vcpu : < { vm_vcpu_length } } \
2018-10-14 02:01:35 -04:00
{ vm_node : < { vm_node_length } } \
2018-09-20 03:25:58 -04:00
{ vm_migrated : < { vm_migrated_length } } { end_bold } ' .format(
vm_name_length = vm_name_length ,
2018-11-02 00:42:44 -04:00
vm_uuid_length = vm_uuid_length ,
vm_state_length = vm_state_length ,
2018-10-14 02:01:35 -04:00
vm_nets_length = vm_nets_length ,
2018-11-02 00:42:44 -04:00
vm_ram_length = vm_ram_length ,
vm_vcpu_length = vm_vcpu_length ,
vm_node_length = vm_node_length ,
2018-09-20 03:25:58 -04:00
vm_migrated_length = vm_migrated_length ,
bold = ' ' ,
end_bold = ' ' ,
vm_state_colour = vm_state_colour ,
2018-10-20 15:28:25 -04:00
end_colour = ansiprint . end ( ) ,
2019-05-20 22:15:28 -04:00
vm_name = domain_information [ ' name ' ] ,
vm_uuid = domain_information [ ' uuid ' ] ,
vm_state = domain_information [ ' state ' ] ,
2019-07-12 12:22:41 -04:00
vm_net_colour = vm_net_colour ,
2019-05-20 22:15:28 -04:00
vm_networks = ' , ' . join ( net_list ) ,
vm_memory = domain_information [ ' memory ' ] ,
vm_vcpu = domain_information [ ' vcpu ' ] ,
vm_node = domain_information [ ' node ' ] ,
vm_migrated = domain_information [ ' migrated ' ]
2018-09-20 03:25:58 -04:00
)
)
click . echo ( ' \n ' . join ( sorted ( vm_list_output ) ) )
return True , ' '
2019-05-20 22:15:28 -04:00