2023-05-20 13:39:45 +02:00
#!/usr/bin/python3
"""
Copyright 2023 , Georg Pfuetzenreuter
Licensed under the EUPL , Version 1.2 or - as soon they will be approved by the European Commission - subsequent versions of the EUPL ( the " Licence " ) .
You may not use this work except in compliance with the Licence .
An English copy of the Licence is shipped in a file called LICENSE along with this applications source code .
You may obtain copies of the Licence in any of the official languages at https : / / joinup . ec . europa . eu / collection / eupl / eupl - text - eupl - 12.
- - -
Scullery - a SaltStack testing tool .
"""
from argparse import ArgumentParser
from configparser import ConfigParser
import logging
import os
import sys
try :
import vagrant
except ImportError as myerror :
print ( ' Could not load python-vagrant ' )
sys . exit ( 1 )
argparser = ArgumentParser ( )
config = ConfigParser ( )
env = os . environ . copy ( )
arggroup = argparser . add_mutually_exclusive_group ( )
argparser . add_argument ( ' --debug ' , help = ' Print extremely verbose output ' , action = ' store_const ' , dest = ' loglevel ' , const = logging . DEBUG , default = logging . INFO )
argparser . add_argument ( ' --config ' , help = ' Specify the configuration file to use ' , default = ' {} /scullery.ini ' . format ( os . path . abspath ( os . path . dirname ( __file__ ) ) ) )
2023-05-20 15:02:28 +02:00
argparser . add_argument ( ' --env ' , help = ' Write environment file for direct use of Vagrant ' , action = ' store_true ' )
2023-05-20 13:39:45 +02:00
argparser . add_argument ( ' --suite ' , help = ' Specify the suite to run ' , required = True )
arggroup . add_argument ( ' --stop ' , help = ' Stop running machines ' , action = ' store_true ' )
arggroup . add_argument ( ' --test ' , help = ' Start machines and run tests ' , action = ' store_true ' )
arggroup . add_argument ( ' --status ' , help = ' Get Vagrant deployment status ' , action = ' store_true ' )
args = argparser . parse_args ( )
config . read ( args . config )
vmprefix = ' scullery '
def _abort ( msg ) :
log . error ( msg )
sys . exit ( 1 )
def _config ( ) :
configmap = { ' box ' : { } , ' suites ' : { } }
if not config . options ( ' box ' ) :
_abort ( ' No " box " section found in the configuration file ' )
for option in config . options ( ' box ' ) :
configmap [ ' box ' ] [ option ] = config . get ( ' box ' , option )
suites = [ section for section in config . sections ( ) if section . startswith ( ' suite. ' ) ]
if not len ( suites ) :
_abort ( ' No suites configured ' )
log . debug ( ' Suites: {} ' . format ( str ( suites ) ) )
for section in suites :
suite = section . replace ( ' suite. ' , ' ' )
configmap [ ' suites ' ] [ suite ] = { }
for option in config . options ( section ) :
if option in [ ' masters ' , ' minions ' ] :
value = config . getint ( section , option )
else :
value = config . get ( section , option )
configmap [ ' suites ' ] [ suite ] [ option ] = value
log . debug ( ' Config map: {} ' . format ( str ( configmap ) ) )
return configmap
def _vagrant ( quiet = False ) :
return vagrant . Vagrant ( quiet_stdout = False , quiet_stderr = quiet )
def genvms ( flavor , amount ) :
vms = [ ]
for i in range ( amount ) :
vms . append ( ' {} - {} {} ' . format ( vmprefix , flavor , i ) )
return vms
2023-05-20 15:02:28 +02:00
def _setenv ( envmap , dump = False ) :
if dump :
log . debug ( ' Writing environment variable file ' )
fh = open ( ' .scullery_env ' , ' w ' )
for variable , vlaue in envmap . items ( ) :
if value is not None :
if isinstance ( value , list ) :
value = ' , ' . join ( value )
env [ variable ] = value
if dump :
fh . write ( f ' { variable } = { value } \n ' )
if dump :
fh . close ( )
2023-05-20 13:39:45 +02:00
2023-05-20 15:02:28 +02:00
def vagrant_env ( box_name , box_image , minions = None , masters = None , vagrantfile = None ) :
envmap = { ' VAGRANT_VAGRANTFILE ' : vagrantfile , ' SCULLERY_BOX_NAME ' : box_name , ' SCULLERY_BOX_IMAGE ' : box_image ,
' SCULLERY_MASTERS ' : masters , ' SCULLERY_MINIONS ' : minions }
log . debug ( ' Environment variable map: {} ' . format ( str ( envmap ) ) )
_setenv ( envmap , args . env )
2023-05-20 13:39:45 +02:00
v . env = env
def vagrant_isup ( suite ) :
ok = 0
nok = 0
statuses = v . status ( )
total = len ( statuses )
for status in statuses :
if status . state == ' running ' :
ok + = 1
else :
nok + = 1
if ok == total :
return True , None
elif nok == total :
return False , True
else :
return False , False
def main_interactive ( ) :
configmap = _config ( )
box = configmap [ ' box ' ]
suites = configmap [ ' suites ' ]
suite = args . suite
if suite not in suites :
_abort ( ' No suite named {} ' . format ( suite ) )
suiteconf = configmap [ ' suites ' ] [ suite ]
minions = None
masters = None
if suiteconf . get ( ' minions ' , 0 ) > 0 :
minions = genvms ( ' minion ' , suiteconf [ ' minions ' ] )
if suiteconf . get ( ' masters ' , 0 ) > 0 :
masters = genvms ( ' master ' , suiteconf [ ' masters ' ] )
vagrant_env ( box [ ' name ' ] , box [ ' image ' ] , minions , masters , box [ ' file ' ] )
if args . status :
log . info ( ' Status report: {} ' . format ( v . status ( ) ) )
return True
status = vagrant_isup ( suite )
if status [ 0 ] is True and status [ 1 ] is None :
if args . stop is True :
log . info ( ' Destroying machines ... ' )
v . destroy ( )
if vagrant_isup ( suite ) [ 0 ] is False :
log . debug ( ' OK ' )
else :
_abort ( ' Destruction failed ' )
else :
log . info ( ' Deployment is already running ' )
elif status [ 0 ] is False :
if status [ 1 ] is True :
log . debug ( ' Deployment is not running ' )
elif status [ 1 ] is False :
log . warning ( ' Deployment is in an inconsistent state, destroying ... ' )
try :
v . destroy ( )
except Exception as myerror :
log . exception ( myerror )
_abort ( ' Unhandled error ' )
if args . stop is False :
log . info ( ' Launching {} ... ' . format ( suite ) )
v . up ( )
if vagrant_isup ( suite ) [ 0 ] is True :
log . debug ( ' OK ' )
else :
_abort ( ' Start failed ' )
if __name__ == ' __main__ ' :
logging . basicConfig ( format = ' %(asctime)s %(levelname)s - %(funcName)s : %(message)s ' , datefmt = ' % H: % M: % S ' )
log = logging . getLogger ( ' scullery ' )
log . setLevel ( args . loglevel )
log . debug ( args )
if args . loglevel == logging . INFO :
log_stderr = True
else :
log_stderr = False
v = _vagrant ( log_stderr )
main_interactive ( )
else :
v = _vagrant ( )