@ -1,6 +1,6 @@
#!/usr/bin/python
#!/usr/bin/python
# (c) 201 3 , Paul Markham <pmarkham@netrefinery.com>
# (c) 201 5 , Paul Markham <pmarkham@netrefinery.com>
#
#
# This file is part of Ansible
# This file is part of Ansible
#
#
@ -37,13 +37,18 @@ options:
state :
state :
required : true
required : true
description :
description :
- C ( present ) , create the zone .
- C ( present ) , configure and install the zone .
- C ( running ) , if the zone already exists , boot it , otherwise , create the zone
- C ( installed ) , synonym for C ( present ) .
first , then boot it .
- C ( running ) , if the zone already exists , boot it , otherwise , configure and install
the zone first , then boot it .
- C ( started ) , synonym for C ( running ) .
- C ( started ) , synonym for C ( running ) .
- C ( stopped ) , shutdown a zone .
- C ( stopped ) , shutdown a zone .
- C ( absent ) , destroy the zone .
- C ( absent ) , destroy the zone .
choices : [ ' present ' , ' started ' , ' running ' , ' stopped ' , ' absent ' ]
- C ( configured ) , configure the ready so that it ' s to be attached.
- C ( attached ) , attach a zone , but do not boot it .
- C ( detach ) , stop and detach a zone
choices : [ ' present ' , ' installed ' , ' started ' , ' running ' , ' stopped ' , ' absent ' , ' configured ' , ' attached ' , ' detached ' ]
default : present
name :
name :
description :
description :
- Zone name .
- Zone name .
@ -68,19 +73,25 @@ options:
config :
config :
required : false
required : false
description :
description :
- ' The zonecfg configuration commands for this zone, separated by commas, e.g.
- ' The zonecfg configuration commands for this zone. See zonecfg(1M) for the valid options
" set auto-boot=true,add net,set physical=bge0,set address=10.1.1.1,end "
and syntax . Typically this is a list of options separated by semi - colons or new lines , e . g .
See the Solaris Systems Administrator guide for a list of all configuration commands
" set auto-boot=true;add net;set physical=bge0;set address=10.1.1.1;end " '
that can be used . '
required : false
required : false
default : null
default : empty string
create_options :
create_options :
required : false
required : false
description :
description :
- ' Extra options to the zonecfg create command. For example, this can be used to create a
- ' Extra options to the zonecfg(1M) create command. '
Solaris 11 kernel zone '
required : false
required : false
default : null
default : empty string
attach_options :
required : false
description :
- ' Extra options to the zoneadm attach command. For example, this can be used to specify
whether a minimum or full update of packages is required and if any packages need to
be deleted . For valid values , see zoneadm ( 1 M ) '
required : false
default : empty string
timeout :
timeout :
description :
description :
- Timeout , in seconds , for zone to boot .
- Timeout , in seconds , for zone to boot .
@ -89,15 +100,15 @@ options:
'''
'''
EXAMPLES = '''
EXAMPLES = '''
# Create a zone, but don't boot it
# Create a nd install a zone, but don't boot it
solaris_zone : name = zone1 state = present path = / zones / zone1 sparse = true root_password = " Be9oX7OSwWoU. "
solaris_zone : name = zone1 state = present path = / zones / zone1 sparse = true root_password = " Be9oX7OSwWoU. "
config = ' set autoboot=true , add net, set physical=bge0, set address=10.1.1.1, end'
config = ' set autoboot=true ; add net; set physical=bge0; set address=10.1.1.1; end'
# Create a zone and boot it
# Create a nd install a zone and boot it
solaris_zone : name = zone1 state = running path = / zones / zone1 root_password = " Be9oX7OSwWoU. "
solaris_zone : name = zone1 state = running path = / zones / zone1 root_password = " Be9oX7OSwWoU. "
config = ' set autoboot=true , add net, set physical=bge0, set address=10.1.1.1, end'
config = ' set autoboot=true ; add net; set physical=bge0; set address=10.1.1.1; end'
# Boot an already creat ed zone
# Boot an already install ed zone
solaris_zone : name = zone1 state = running
solaris_zone : name = zone1 state = running
# Stop a zone
# Stop a zone
@ -105,6 +116,16 @@ solaris_zone: name=zone1 state=stopped
# Destroy a zone
# Destroy a zone
solaris_zone : name = zone1 state = absent
solaris_zone : name = zone1 state = absent
# Detach a zone
solaris_zone : name = zone1 state = detached
# Configure a zone, ready to be attached
solaris_zone : name = zone1 state = configured path = / zones / zone1 root_password = " Be9oX7OSwWoU. "
config = ' set autoboot=true; add net; set physical=bge0; set address=10.1.1.1; end '
# Attach a zone
solaris_zone : name = zone1 state = attached attach_options = ' -u '
'''
'''
class Zone ( object ) :
class Zone ( object ) :
@ -120,45 +141,60 @@ class Zone(object):
self . timeout = self . module . params [ ' timeout ' ]
self . timeout = self . module . params [ ' timeout ' ]
self . config = self . module . params [ ' config ' ]
self . config = self . module . params [ ' config ' ]
self . create_options = self . module . params [ ' create_options ' ]
self . create_options = self . module . params [ ' create_options ' ]
self . attach_options = self . module . params [ ' attach_options ' ]
self . zoneadm_cmd = self . module . get_bin_path ( ' zoneadm ' , True )
self . zoneadm_cmd = self . module . get_bin_path ( ' zoneadm ' , True )
self . zonecfg_cmd = self . module . get_bin_path ( ' zonecfg ' , True )
self . zonecfg_cmd = self . module . get_bin_path ( ' zonecfg ' , True )
self . ssh_keygen_cmd = self . module . get_bin_path ( ' ssh-keygen ' , True )
self . ssh_keygen_cmd = self . module . get_bin_path ( ' ssh-keygen ' , True )
def c reat e( self ) :
def c onfigu re( self ) :
if not self . path :
if not self . path :
self . module . fail_json ( msg = ' Missing required argument: path ' )
self . module . fail_json ( msg = ' Missing required argument: path ' )
t = tempfile . NamedTemporaryFile ( delete = False )
if not self . module . check_mode :
t = tempfile . NamedTemporaryFile ( delete = False )
if self . sparse :
if self . sparse :
t . write ( ' create %s \n ' % self . create_options )
t . write ( ' create %s \n ' % self . create_options )
self . msg . append ( ' creating sparse root zone ' )
self . msg . append ( ' creating sparse-root zone ' )
else :
else :
t . write ( ' create -b %s \n ' % self . create_options )
t . write ( ' create -b %s \n ' % self . create_options )
self . msg . append ( ' creating whole root zone ' )
self . msg . append ( ' creating whole-root zone ' )
t . write ( ' set zonepath= %s \n ' % self . path )
if self . config :
t . write ( ' set zonepath= %s \n ' % self . path )
for line in self . config :
t . write ( ' %s \n ' % self . config )
t . write ( ' %s \n ' % line )
t . close ( )
t . close ( )
cmd = ' %s -z %s -f %s ' % ( self . zonecfg_cmd , self . name , t . name )
cmd = ' %s -z %s -f %s ' % ( self . zonecfg_cmd , self . name , t . name )
( rc , out , err ) = self . module . run_command ( cmd )
( rc , out , err ) = self . module . run_command ( cmd )
if rc != 0 :
if rc != 0 :
self . module . fail_json ( msg = ' Failed to create zone. %s ' % ( out + err ) )
self . module . fail_json ( msg = ' Failed to create zone. %s ' % ( out + err ) )
os . unlink ( t . name )
os . unlink ( t . name )
cmd = ' %s -z %s install ' % ( self . zoneadm_cmd , self . name )
self . changed = True
( rc , out , err ) = self . module . run_command ( cmd )
self . msg . append ( ' zone configured ' )
if rc != 0 :
self . module . fail_json ( msg = ' Failed to install zone. %s ' % ( out + err ) )
self . configure_sysid ( )
def install ( self ) :
self . configure_password ( )
if not self . module . check_mode :
self . configure_ssh_keys ( )
cmd = ' %s -z %s install ' % ( self . zoneadm_cmd , self . name )
( rc , out , err ) = self . module . run_command ( cmd )
if rc != 0 :
self . module . fail_json ( msg = ' Failed to install zone. %s ' % ( out + err ) )
self . configure_sysid ( )
self . configure_password ( )
self . configure_ssh_keys ( )
self . changed = True
self . msg . append ( ' zone installed ' )
def uninstall ( self ) :
if self . is_installed ( ) :
if not self . module . check_mode :
cmd = ' %s -z %s uninstall -F ' % ( self . zoneadm_cmd , self . name )
( rc , out , err ) = self . module . run_command ( cmd )
if rc != 0 :
self . module . fail_json ( msg = ' Failed to uninstall zone. %s ' % ( out + err ) )
self . changed = True
self . msg . append ( ' zone uninstalled ' )
def configure_sysid ( self ) :
def configure_sysid ( self ) :
if os . path . isfile ( ' %s /root/etc/.UNCONFIGURED ' % self . path ) :
if os . path . isfile ( ' %s /root/etc/.UNCONFIGURED ' % self . path ) :
@ -220,39 +256,70 @@ class Zone(object):
f . close ( )
f . close ( )
def boot ( self ) :
def boot ( self ) :
cmd = ' %s -z %s boot ' % ( self . zoneadm_cmd , self . name )
if not self . module . check_mode :
( rc , out , err ) = self . module . run_command ( cmd )
cmd = ' %s -z %s boot ' % ( self . zoneadm_cmd , self . name )
if rc != 0 :
( rc , out , err ) = self . module . run_command ( cmd )
self . module . fail_json ( msg = ' Failed to boot zone. %s ' % ( out + err ) )
if rc != 0 :
self . module . fail_json ( msg = ' Failed to boot zone. %s ' % ( out + err ) )
"""
The boot command can return before the zone has fully booted . This is especially
"""
true on the first boot when the zone initializes the SMF services . Unless the zone
The boot command can return before the zone has fully booted . This is especially
has fully booted , subsequent tasks in the playbook may fail as services aren ' t running yet.
true on the first boot when the zone initializes the SMF services . Unless the zone
Wait until the zone ' s console login is running; once that ' s running , consider the zone booted .
has fully booted , subsequent tasks in the playbook may fail as services aren ' t running yet.
"""
Wait until the zone ' s console login is running; once that ' s running , consider the zone booted .
"""
elapsed = 0
while True :
elapsed = 0
if elapsed > self . timeout :
while True :
self . module . fail_json ( msg = ' timed out waiting for zone to boot ' )
if elapsed > self . timeout :
rc = os . system ( ' ps -z %s -o args|grep " /usr/lib/saf/ttymon.*-d /dev/console " > /dev/null 2>/dev/null ' % self . name )
self . module . fail_json ( msg = ' timed out waiting for zone to boot ' )
if rc == 0 :
rc = os . system ( ' ps -z %s -o args|grep " /usr/lib/saf/ttymon.*-d /dev/console " > /dev/null 2>/dev/null ' % self . name )
break
if rc == 0 :
time . sleep ( 10 )
break
elapsed + = 10
time . sleep ( 10 )
elapsed + = 10
self . changed = True
self . msg . append ( ' zone booted ' )
def destroy ( self ) :
def destroy ( self ) :
cmd = ' %s -z %s delete -F ' % ( self . zonecfg_cmd , self . name )
if self . is_running ( ) :
( rc , out , err ) = self . module . run_command ( cmd )
self . stop ( )
if rc != 0 :
if self . is_installed ( ) :
self . module . fail_json ( msg = ' Failed to delete zone. %s ' % ( out + err ) )
self . uninstall ( )
if not self . module . check_mode :
cmd = ' %s -z %s delete -F ' % ( self . zonecfg_cmd , self . name )
( rc , out , err ) = self . module . run_command ( cmd )
if rc != 0 :
self . module . fail_json ( msg = ' Failed to delete zone. %s ' % ( out + err ) )
self . changed = True
self . msg . append ( ' zone deleted ' )
def stop ( self ) :
def stop ( self ) :
cmd = ' %s -z %s halt ' % ( self . zoneadm_cmd , self . name )
if not self . module . check_mode :
( rc , out , err ) = self . module . run_command ( cmd )
cmd = ' %s -z %s halt ' % ( self . zoneadm_cmd , self . name )
if rc != 0 :
( rc , out , err ) = self . module . run_command ( cmd )
self . module . fail_json ( msg = ' Failed to stop zone. %s ' % ( out + err ) )
if rc != 0 :
self . module . fail_json ( msg = ' Failed to stop zone. %s ' % ( out + err ) )
self . changed = True
self . msg . append ( ' zone stopped ' )
def detach ( self ) :
if not self . module . check_mode :
cmd = ' %s -z %s detach ' % ( self . zoneadm_cmd , self . name )
( rc , out , err ) = self . module . run_command ( cmd )
if rc != 0 :
self . module . fail_json ( msg = ' Failed to detach zone. %s ' % ( out + err ) )
self . changed = True
self . msg . append ( ' zone detached ' )
def attach ( self ) :
if not self . module . check_mode :
cmd = ' %s -z %s attach %s ' % ( self . zoneadm_cmd , self . name , self . attach_options )
( rc , out , err ) = self . module . run_command ( cmd )
if rc != 0 :
self . module . fail_json ( msg = ' Failed to attach zone. %s ' % ( out + err ) )
self . changed = True
self . msg . append ( ' zone attached ' )
def exists ( self ) :
def exists ( self ) :
cmd = ' %s -z %s list ' % ( self . zoneadm_cmd , self . name )
cmd = ' %s -z %s list ' % ( self . zoneadm_cmd , self . name )
@ -262,74 +329,85 @@ class Zone(object):
else :
else :
return False
return False
def running ( self ) :
def is_running ( self ) :
return self . status ( ) == ' running '
def is_installed ( self ) :
return self . status ( ) == ' installed '
def is_configured ( self ) :
return self . status ( ) == ' configured '
def status ( self ) :
cmd = ' %s -z %s list -p ' % ( self . zoneadm_cmd , self . name )
cmd = ' %s -z %s list -p ' % ( self . zoneadm_cmd , self . name )
( rc , out , err ) = self . module . run_command ( cmd )
( rc , out , err ) = self . module . run_command ( cmd )
if rc != 0 :
if rc != 0 :
self . module . fail_json ( msg = ' Failed to determine zone state. %s ' % ( out + err ) )
self . module . fail_json ( msg = ' Failed to determine zone state. %s ' % ( out + err ) )
return out . split ( ' : ' ) [ 2 ]
if out . split ( ' : ' ) [ 2 ] == ' running ' :
return True
else :
return False
def state_present ( self ) :
def state_present ( self ) :
if self . exists ( ) :
if self . exists ( ) :
self . msg . append ( ' zone already exists ' )
self . msg . append ( ' zone already exists ' )
else :
else :
if not self . module . check_mode :
self . configure ( )
self . create ( )
self . install ( )
self . changed = True
self . msg . append ( ' zone created ' )
def state_running ( self ) :
def state_running ( self ) :
self . state_present ( )
self . state_present ( )
if self . running( ) :
if self . is_ running( ) :
self . msg . append ( ' zone already running ' )
self . msg . append ( ' zone already running ' )
else :
else :
if not self . module . check_mode :
self . boot ( )
self . boot ( )
self . changed = True
self . msg . append ( ' zone booted ' )
def state_stopped ( self ) :
def state_stopped ( self ) :
if self . exists ( ) :
if self . exists ( ) :
if self . running ( ) :
self . stop ( )
if not self . module . check_mode :
self . stop ( )
self . changed = True
self . msg . append ( ' zone stopped ' )
else :
self . msg . append ( ' zone not running ' )
else :
else :
self . module . fail_json ( msg = ' zone does not exist ' )
self . module . fail_json ( msg = ' zone does not exist ' )
def state_absent ( self ) :
def state_absent ( self ) :
if self . exists ( ) :
if self . exists ( ) :
self . state_stopped ( )
if self . is_running ( ) :
if not self . module . check_mode :
self . stop ( )
self . destroy ( )
self . destroy ( )
self . changed = True
self . msg . append ( ' zone deleted ' )
else :
else :
self . msg . append ( ' zone does not exist ' )
self . msg . append ( ' zone does not exist ' )
def exit_with_msg ( self ) :
def state_configured ( self ) :
msg = ' , ' . join ( self . msg )
if self . exists ( ) :
self . module . exit_json ( changed = self . changed , msg = msg )
self . msg . append ( ' zone already exists ' )
else :
self . configure ( )
def state_detached ( self ) :
if not self . exists ( ) :
self . module . fail_json ( msg = ' zone does not exist ' )
if self . is_configured ( ) :
self . msg . append ( ' zone already detached ' )
else :
self . stop ( )
self . detach ( )
def state_attached ( self ) :
if not self . exists ( ) :
self . msg . append ( ' zone does not exist ' )
if self . is_configured ( ) :
self . attach ( )
else :
self . msg . append ( ' zone already attached ' )
def main ( ) :
def main ( ) :
module = AnsibleModule (
module = AnsibleModule (
argument_spec = dict (
argument_spec = dict (
name = dict ( required = True ) ,
name = dict ( required = True ) ,
state = dict ( required = True , choices = [ ' running ' , ' started ' , ' present ' , ' stopped ' , ' absent ' ] ) ,
state = dict ( default= ' present ' , choices = [ ' running ' , ' started ' , ' present ' , ' installed' , ' stopped' , ' absent ' , ' configured ' , ' detached ' , ' attached ' ] ) ,
path = dict ( defalt = None ) ,
path = dict ( defalt = None ) ,
sparse = dict ( default = False , type = ' bool ' ) ,
sparse = dict ( default = False , type = ' bool ' ) ,
root_password = dict ( default = None ) ,
root_password = dict ( default = None ) ,
timeout = dict ( default = 600 , type = ' int ' ) ,
timeout = dict ( default = 600 , type = ' int ' ) ,
config = dict ( default = None , type = ' list ' ) ,
config = dict ( default = ' ' ) ,
create_options = dict ( default = ' ' ) ,
create_options = dict ( default = ' ' ) ,
attach_options = dict ( default = ' ' ) ,
) ,
) ,
supports_check_mode = True
supports_check_mode = True
)
)
@ -347,16 +425,22 @@ def main():
if state == ' running ' or state == ' started ' :
if state == ' running ' or state == ' started ' :
zone . state_running ( )
zone . state_running ( )
elif state == ' present ' :
elif state == ' present ' or state == ' installed ' :
zone . state_present ( )
zone . state_present ( )
elif state == ' stopped ' :
elif state == ' stopped ' :
zone . state_stopped ( )
zone . state_stopped ( )
elif state == ' absent ' :
elif state == ' absent ' :
zone . state_absent ( )
zone . state_absent ( )
elif state == ' configured ' :
zone . state_configured ( )
elif state == ' detached ' :
zone . state_detached ( )
elif state == ' attached ' :
zone . state_attached ( )
else :
else :
module . fail_json ( msg = ' Invalid state: %s ' % state )
module . fail_json ( msg = ' Invalid state: %s ' % state )
zone . exit_with_msg ( )
module. exit_json ( changed = zone . changed , msg = ' , ' . join ( zone . msg ) )
from ansible . module_utils . basic import *
from ansible . module_utils . basic import *
main ( )
main ( )