
    Vhd                         d dl mZmZmZ eZdZdZdZd dl	m	Z	m
Z
 d dlmZ d dlmZ d dlmZ d	d
lmZmZ dZ G d de      Zd Zedk(  r e        yy)    )absolute_importdivisionprint_functionu  
---
module: server
short_description: Manages servers on the cloudscale.ch IaaS service
description:
  - Create, update, start, stop and delete servers on the cloudscale.ch IaaS service.
notes:
  - If I(uuid) option is provided, it takes precedence over I(name) for server selection. This allows to update the server's name.
  - If no I(uuid) option is provided, I(name) is used for server selection. If more than one server with this name exists, execution is aborted.
  - Only the I(name) and I(flavor) are evaluated for the update.
  - The option I(force=true) must be given to allow the reboot of existing running servers for applying the changes.
author:
  - Gaudenz Steinlin (@gaudenz)
  - René Moser (@resmo)
  - Denis Krienbühl (@href)
version_added: "1.0.0"
options:
  state:
    description:
      - State of the server.
    choices: [ running, stopped, absent ]
    default: running
    type: str
  name:
    description:
      - Name of the Server.
      - Either I(name) or I(uuid) are required.
    type: str
  uuid:
    description:
      - UUID of the server.
      - Either I(name) or I(uuid) are required.
    type: str
  flavor:
    description:
      - Flavor of the server.
    type: str
  image:
    description:
      - Image used to create the server.
    type: str
  zone:
    description:
      - Zone in which the server resides (e.g. C(lpg1) or C(rma1)).
    type: str
  volume_size_gb:
    description:
      - Initial size of the root volume in GB.
      - This parameter has no effect on existing servers.
      - Use the volume module to change the size of the root volume.
    default: 10
    type: int
  bulk_volume_size_gb:
    description:
      - Size of the bulk storage volume in GB.
      - No bulk storage volume if not set.
    type: int
  ssh_keys:
    description:
       - List of SSH public keys.
       - Use the full content of your .pub file here.
    type: list
    elements: str
  password:
    description:
       - Password for the server.
    type: str
  use_public_network:
    description:
      - Attach a public network interface to the server.
    type: bool
  use_private_network:
    description:
      - Attach a private network interface to the server.
    type: bool
  use_ipv6:
    description:
      - Enable IPv6 on the public network interface.
    default: true
    type: bool
  interfaces:
    description:
      - List of network interface objects specifying the interfaces to be attached to the server.
        See U(https://www.cloudscale.ch/en/api/v1/#interfaces-attribute-specification) for more details.
    type: list
    elements: dict
    version_added: 1.4.0
    suboptions:
      network:
        description:
          - Create a network interface on the network identified by UUID.
            Use 'public' instead of an UUID to attach a public network interface.
            Can be omitted if a subnet is provided under addresses.
        type: str
      addresses:
        description:
          - Attach a private network interface and configure a subnet and/or an IP address.
        type: list
        elements: dict
        suboptions:
          subnet:
            description:
              - UUID of the subnet from which an address will be assigned.
            type: str
          address:
            description:
              - The static IP address of the interface. Use '[]' to avoid assigning an IP address via DHCP.
            type: str
  server_groups:
    description:
      - List of UUID or names of server groups.
    type: list
    elements: str
  user_data:
    description:
      - Cloud-init configuration (cloud-config) data to use for the server.
    type: str
  force:
    description:
      - Allow to stop the running server for updating if necessary.
    default: false
    type: bool
  tags:
    description:
      - Tags assosiated with the servers. Set this to C({}) to clear any tags.
    type: dict
extends_documentation_fragment: cloudscale_ch.cloud.api_parameters
aW  
# Create and start a server with an existing server group (shiny-group)
- name: Start cloudscale.ch server
  cloudscale_ch.cloud.server:
    name: my-shiny-cloudscale-server
    image: debian-10
    flavor: flex-4-4
    ssh_keys:
      - ssh-rsa XXXXXXXXXX...XXXX ansible@cloudscale
    server_groups: shiny-group
    zone: lpg1
    use_private_network: true
    bulk_volume_size_gb: 100
    api_token: xxxxxx

# Start another server in anti-affinity (server group shiny-group)
- name: Start second cloudscale.ch server
  cloudscale_ch.cloud.server:
    name: my-other-shiny-server
    image: ubuntu-16.04
    flavor: flex-8-2
    ssh_keys:
      - ssh-rsa XXXXXXXXXX...XXXX ansible@cloudscale
    server_groups: shiny-group
    zone: lpg1
    api_token: xxxxxx

# Force to update the flavor of a running server
- name: Start cloudscale.ch server
  cloudscale_ch.cloud.server:
    name: my-shiny-cloudscale-server
    image: debian-10
    flavor: flex-8-2
    force: true
    ssh_keys:
      - ssh-rsa XXXXXXXXXX...XXXX ansible@cloudscale
    use_private_network: true
    bulk_volume_size_gb: 100
    api_token: xxxxxx
  register: server1

# Stop the first server
- name: Stop my first server
  cloudscale_ch.cloud.server:
    uuid: '{{ server1.uuid }}'
    state: stopped
    api_token: xxxxxx

# Delete my second server
- name: Delete my second server
  cloudscale_ch.cloud.server:
    name: my-other-shiny-server
    state: absent
    api_token: xxxxxx

# Start a server and wait for the SSH host keys to be generated
- name: Start server and wait for SSH host keys
  cloudscale_ch.cloud.server:
    name: my-cloudscale-server-with-ssh-key
    image: debian-10
    flavor: flex-4-2
    ssh_keys:
      - ssh-rsa XXXXXXXXXX...XXXX ansible@cloudscale
    api_token: xxxxxx
  register: server
  until: server is not failed
  retries: 5
  delay: 2

# Start a server with two network interfaces:
#
#    A public interface with IPv4/IPv6
#    A private interface on a specific private network with an IPv4 address

- name: Start a server with a public and private network interface
  cloudscale_ch.cloud.server:
    name: my-cloudscale-server-with-two-network-interfaces
    image: debian-10
    flavor: flex-4-2
    ssh_keys:
      - ssh-rsa XXXXXXXXXX...XXXX ansible@cloudscale
    api_token: xxxxxx
    interfaces:
      - network: 'public'
      - addresses:
        - subnet: UUID_of_private_subnet

# Start a server with a specific IPv4 address from subnet range
- name: Start a server with a specific IPv4 address from subnet range
  cloudscale_ch.cloud.server:
    name: my-cloudscale-server-with-specific-address
    image: debian-10
    flavor: flex-4-2
    ssh_keys:
      - ssh-rsa XXXXXXXXXX...XXXX ansible@cloudscale
    api_token: xxxxxx
    interfaces:
      - addresses:
        - subnet: UUID_of_private_subnet
          address: 'A.B.C.D'

# Start a server with two network interfaces:
#
#    A public interface with IPv4/IPv6
#    A private interface on a specific private network with no IPv4 address

- name: Start a server with a private network interface and no IP address
  cloudscale_ch.cloud.server:
    name: my-cloudscale-server-with-specific-address
    image: debian-10
    flavor: flex-4-2
    ssh_keys:
      - ssh-rsa XXXXXXXXXX...XXXX ansible@cloudscale
    api_token: xxxxxx
    interfaces:
      - network: 'public'
      - network: UUID_of_private_network
        addresses: []
aB	  
href:
  description: API URL to get details about this server
  returned: success when not state == absent
  type: str
  sample: https://api.cloudscale.ch/v1/servers/cfde831a-4e87-4a75-960f-89b0148aa2cc
uuid:
  description: The unique identifier for this server
  returned: success
  type: str
  sample: cfde831a-4e87-4a75-960f-89b0148aa2cc
name:
  description: The display name of the server
  returned: success
  type: str
  sample: its-a-me-mario.cloudscale.ch
state:
  description: The current status of the server
  returned: success
  type: str
  sample: running
flavor:
  description: The flavor that has been used for this server
  returned: success when not state == absent
  type: dict
  sample: { "slug": "flex-4-2", "name": "Flex-4-2", "vcpu_count": 2, "memory_gb": 4 }
image:
  description: The image used for booting this server
  returned: success when not state == absent
  type: dict
  sample: { "default_username": "ubuntu", "name": "Ubuntu 18.04 LTS", "operating_system": "Ubuntu", "slug": "ubuntu-18.04" }
zone:
  description: The zone used for booting this server
  returned: success when not state == absent
  type: dict
  sample: { 'slug': 'lpg1' }
volumes:
  description: List of volumes attached to the server
  returned: success when not state == absent
  type: list
  sample: [ {"type": "ssd", "device": "/dev/vda", "size_gb": "50"} ]
interfaces:
  description: List of network ports attached to the server
  returned: success when not state == absent
  type: list
  sample: [ { "type": "public", "addresses": [ ... ] } ]
ssh_fingerprints:
  description: A list of SSH host key fingerprints. Will be null until the host keys could be retrieved from the server.
  returned: success when not state == absent
  type: list
  sample: ["ecdsa-sha2-nistp256 SHA256:XXXX", ... ]
ssh_host_keys:
  description: A list of SSH host keys. Will be null until the host keys could be retrieved from the server.
  returned: success when not state == absent
  type: list
  sample: ["ecdsa-sha2-nistp256 XXXXX", ... ]
server_groups:
  description: List of server groups
  returned: success when not state == absent
  type: list
  sample: [ {"href": "https://api.cloudscale.ch/v1/server-groups/...", "uuid": "...", "name": "db-group"} ]
tags:
  description: Tags assosiated with the server.
  returned: success
  type: dict
  sample: { 'project': 'my project' }
)datetime	timedelta)sleep)deepcopy)AnsibleModule   )AnsibleCloudscaleBasecloudscale_argument_spec)runningstoppedabsentc                   |     e Zd Z fdZd ZddZed        Zd ZddZ	ddZ
d Zd	 Zd
 Zd Zd Zd Zd Z xZS )AnsibleCloudscaleServerc                 :    t         t        |   |       i | _        y )N)superr   __init___info)selfmodule	__class__s     n/home/dcms/DCMS/lib/python3.12/site-packages/ansible_collections/cloudscale_ch/cloud/plugins/modules/server.pyr   z AnsibleCloudscaleServer.__init__[  s    %t5f= 
    c                    | j                   j                  j                  d      xs | j                  j                  d      | j                   j                  j                  d      xs | j                  j                  d      ddS )Nuuidnamer   )r   r   state)_moduleparamsgetr   )r   s    r   _init_server_containerz.AnsibleCloudscaleServer._init_server_containera  sc    LL''++F3Mtzz~~f7MLL''++F3Mtzz~~f7M
 	
r   c                    | j                   r|s| j                   S | j                         | _         | j                   j                  d      }|8| j                  d|z        }|r| j	                  |      | _         | j                   S | j                   j                  d      }|| j                  d      xs g }g }|D ]  }|d   |k(  s|j                  |        t        |      dk(  r%| j	                  |d         | _         | j                   S t        |      dkD  r| j                  j                  d|z         | j                   S )	Nr   
servers/%sr   servers   r   z\More than one server with name '%s' exists. Use the 'uuid' parameter to identify the server.msg)	r   r#   r"   _get_transform_stateappendlenr    	fail_json)r   refreshr   server_infor   r&   matching_serverservers           r   _get_server_infoz(AnsibleCloudscaleServer._get_server_infoh  s>   ::g::002
zz~~f%))L4$78K!22;?
" zz ::>>&)D))I.4""$% 7Ff~-'..v67 '1,!%!6!6q7I!JDJ
 zz	 )A-LL** 0^`d0e* f zzr   c                 2    d| v r| d   | d<   | d= | S d| d<   | S )Nstatusr   r    )r2   s    r   r+   z(AnsibleCloudscaleServer._transform_state  s7    v$X.F7Ox   'F7Or   c                    t        j                         }| j                  j                  d   dz  }t        j                         |z
  t	        |      k  rW| j                  d      }|j                  d      |v r|S t        d       t        j                         |z
  t	        |      k  rWj                  d      +d	|j                  d      d
|d|j                  d      d}nQ| j                  j                  j                  d      xs% | j                  j                  j                  d      }d|z  }| j                  j                  |       y )Napi_timeoutr   )secondsT)r/   r   r'   r   z3Timeout while waiting for a state change on server z to states z. Current state is .r   z+Timeout while waiting to find the server %sr(   )	r   nowr    r!   r   r3   r"   r   r.   )r   statesstarttimeoutr0   r)   	name_uuids          r   _wait_for_statez'AnsibleCloudscaleServer._wait_for_state  s   ,,%%m4q8llnu$y'AA///=Kw'61""!H	 llnu$y'AA ??6".,7OOF,CV[__]dMegC ++//7Z4<<;N;N;R;RSY;ZI?)KC3'r   c                    ddd}|j                  d      }||k7  rd| j                  d<   |sU| j                  d   d   j                  d|j                  d      i       | j                  d   d	   j                  d|i       | j                  j                  s/| j                  d
|d   d||          | j                  |f      }|S )Nstopr=   r   r   r   Tchangeddiffbeforeafterzservers/r   /)r"   _resultupdater    
check_mode_postr@   )r   r0   target_stateignore_diffactionsserver_states         r   _start_stop_serverz*AnsibleCloudscaleServer._start_stop_server  s    

 #w/<'&*DLL#V$X.55[__W57  V$W-44\6  <<**

k&.A7<CXYZ"22L3CDr   c                    | j                   j                  j                  |      }||S d||   v r	||   d   }n||   }||k7  r| j                  d   d   j	                  ||i       | j                  d   d   j	                  ||i       |j                  d      dk(  rJ|rH| j                   j                  j                  d      s#| j                   j                  d|d	   z         |S d
| j                  d<   | j                   j                  sC|r| j                  |dd
       ||i}| j                  d|d   z  |       | j                  d      }|S )NslugrE   rF   rG   r   r   forcezpSome changes won't be applied to running servers. Use force=true to allow the server '%s' to be stopped/started.r   TrD   r   rM   rN   r%   r   rC   )
r    r!   r"   rI   rJ   warnrK   rQ   _patchr@   )r   	param_keyr0   requires_stopparam_valueserver_v
patch_datas          r   _update_paramz%AnsibleCloudscaleServer._update_param  sh   ll))--i8[++"9-f5H"9-H{"LL *119h2GHLL )00)[1IJw'94 )<)<)@)@)ILL%% 'gitu{i|'} ~&& '+DLL#<<** ++Ki]a+b {

 L;v+>>
K #223IJr   c                    | j                   j                  d   }|sy g }g }| j                  d      }|D ]  }|d   |v r)|j                  |d          |j	                  |d          3|d   |v r=|j                  |d          |j	                  |d          |j                  |d          w|d   |v s| j                   j                  d|d   z          |r.| j                   j                  ddj                  |      z         |S )	Nserver_groupszserver-groupsr   r   ziMore than one server group with name exists: '%s'. Use the 'uuid' parameter to identify the server group.r(   z'Server group name or UUID not found: %sz, )r    r!   r*   r,   remover.   join)r   server_group_paramsmatching_group_namesresultsr_   server_groups         r   _get_server_group_idsz-AnsibleCloudscaleServer._get_server_group_ids  s+   "ll11/B"!		/2) 	xLF#'::|F34#**<+?@f%)<<|F34#**<+?@$++L,@A f%)==&& ,`bnoubv,w& x	x  LL""'PSWS\S\]pSq'q"rr   c                    d| j                   d<   | j                          t        | j                  j                        }dD ]  }||=  | j                         |d<   | j                         | j                   d   d<   t        |      | j                   d   d<   | j                  j                  s#| j                  d|       | j                  d	      }|S )
NTrD   )r   r   rT   r8   	api_tokenapi_urlr_   rE   rF   rG   r&   )r   )
rI   normalize_interfaces_paramr	   r    r!   rf   r#   rK   rL   r@   )r   r0   datais       r   _create_serverz&AnsibleCloudscaleServer._create_server  s    "&Y'')++,R 	AQ	 $ : : <_)-)D)D)FVX&(0VW%||&&JJy$'..}=Kr   c                 
   |j                  d      }| j                         }|5|d   D cg c]  }|d   	 }}||k7  r| j                  j                  d       | j	                          | j                  j
                  j                  d      }|j                  d      }	 | j                  ||       }r6| j                  d|      }| j                  d	   s|d   |k7  | j                  d	<   | j                  d
|d      }| j                  d|      }| j                  d|      }|dk(  r| j                  |dd      }|S c c}w # t        $ r6}	| j                  j                  d|	j                  d   z         Y d }	~	d }	~	ww xY w)Nr   r_   r   zMServer groups can not be mutated, server needs redeployment to change groups.
interfacesz,Error checking 'interfaces', missing key: %sr   r(   rD   flavorT)rY   r   tagsr   rU   )r"   rf   r    rV   rj   r!   has_wanted_interfacesKeyErrorr.   argsr]   rI   rQ   )
r   r0   previous_statedesired_server_group_idsgrpcurrent_server_group_idswantedactualupdate_interfaceses
             r   _update_serverz&AnsibleCloudscaleServer._update_server  s   $1 $(#=#=#? #/?J??['\F'\$'\'+CC!!"qr 	'')
 $$((6.	P$($>$>vv$N N
 ,,\;GK<<	**5l*Cv*MY'((;d(S((=((=Y&11+Icg1hKA (]  	PLL""BQVVAYN # P P	Ps   D> E 	F,E==Fc                    | j                         }|j                  d      dk7  r| j                  j                  j                  d      dk(  r| j	                  |d      }| j                  |      }| j                  j                  j                  d      dk(  r| j	                  |d      }|S | j                  |      }| j	                  || j                  j                  j                  d            }|S )Nr   r   r   )rM   r   )r3   r"   r    r!   rQ   r}   rm   r   r0   s     r   present_serverz&AnsibleCloudscaleServer.present_server7  s    ++-??7#x/ ||""&&w/9<"55kPY5Z--k:K||""&&w/9<"55kPY5Z
  --k:K11+DLLL_L_LcLcdkLl1mKr   c                 ^   | j                         }|j                  d      dk7  rd| j                  d<   t        |      | j                  d   d<   | j	                         | j                  d   d<   | j
                  j                  s(| j                  d|d	   z         | j                  d
      }|S )Nr   r   TrD   rE   rF   rG   r%   r   )r   )	r3   r"   rI   r	   r#   r    rK   _deleter@   r   s     r   absent_serverz%AnsibleCloudscaleServer.absent_serverJ  s    ++-??7#x/&*DLL#-5k-BDLL *,0,G,G,IDLL )<<**\K,??@"22<@r   c                 r    t        |xs d      t        xs d      k7  ryfd}|D ]  } ||      r y y)zs Compares the interfaces as specified by the user, with the
        interfaces as reported by the server.

        r6   Fc                    D ]  }| j                  d      dk(  r
|d   dk(  r nm| j                  d      |d   dk(  r|d   d   | d   k(  r nDt        d | j                  d      xs dD              }t        d	 |d   D              }||k(  s n y
| j                  d      xs dD ]%  }d|vrt        d |d   D              }|d   |vs% y
 | j                  d      g k(  r	|d   g k7  ry
|d   g k(  r| j                  d      g k7  ry
|S )Nnetworkpublictypeprivater   c              3   &   K   | ]	  }|d      yw)subnetNr6   .0as     r   	<genexpr>zYAnsibleCloudscaleServer.has_wanted_interfaces.<locals>.match_interface.<locals>.<genexpr>o  s      (H$%AhK(H   	addressesr6   c              3   ,   K   | ]  }|d    d     yw)r   r   Nr6   r   s     r   r   zYAnsibleCloudscaleServer.has_wanted_interfaces.<locals>.match_interface.<locals>.<genexpr>r  s       (I,-AhK'(Is   Faddressc              3   &   K   | ]	  }|d      yw)r   Nr6   r   s     r   r   zYAnsibleCloudscaleServer.has_wanted_interfaces.<locals>.match_interface.<locals>.<genexpr>  s     M)Mr   )r"   set)spec	interfacewanted_subnet_idsactual_subnet_idswanted_addrr   rz   s         r   match_interfacezFAnsibleCloudscaleServer.has_wanted_interfaces.<locals>.match_interface^  sg    $ 	 88I&(2 (H4 88I&2 (I5$Y/74	?J! %( (H*.((;*?*E2(H %H! %( (I1:;1G(I %I! %(99+.  !% 5 ; ! K/Mi6LMM	y): ! xx$*y/E/K%+0E0Kr   T)r-   )r   ry   rz   r   r   s     `  r   rr   z-AnsibleCloudscaleServer.has_wanted_interfacesU  sK     v|FLb 11/	b  	D #4(	 r   c                     | j                   j                  j                  d      xs dD ]=  }|d   |d= |d   |d= |j                  d      xs dD ]  }|d   |d= |d   |d=  ? y)zX Goes through the interfaces parameter and gets it ready to be
        sent to the API. ro   r6   r   Nr   r   r   )r    r!   r"   )r   r   r   s      r   rj   z2AnsibleCloudscaleServer.normalize_interfaces_param  s     \\((,,\:@b 
	*DK (%I&O HH[17R *9%-	*8$,)	*
	*r   )F)r   F)__name__
__module____qualname__r   r#   r3   staticmethodr+   r@   rQ   r]   rf   rm   r}   r   r   rr   rj   __classcell__)r   s   @r   r   r   Y  s\    
:  (&.&P: (T&	AF*r   r   c                     t               } | j                  t        d(i dt        dt              dt               dt               dt               dt               dt               d	t        d
d      dt        d
      dt        ddd      dt        d      dt        d      dt        d      dt        dd      dt        ddt        t        d      t        ddt        t        d      t        d                              d t        dd!      d"t               d#t        dd      d$t        d             t	        | ddgddgfd%d&      }t        |      }|j                  d   d'k(  r|j                         }n|j                         }|j                  |      } |j                  d(i | y ))Nr   r   )defaultchoicesr   r   rp   imagezonevolume_size_gbint
   )r   r   bulk_volume_size_gb)r   ssh_keysliststrF)r   elementsno_logpasswordT)r   use_public_networkbooluse_private_networkuse_ipv6ro   dict)r   r   )r   r   options)r   r   r_   )r   r   	user_datarT   rq   ))r   r   )argument_specmutually_exclusiverequired_one_ofsupports_check_moder   r6   )r   rJ   r   ALLOWED_STATESr
   r   r!   r   r   
get_result	exit_json)r   r   cloudscale_serverr2   results        r   mainr     s   ,.M !9n=!V! V! v	!
 f! V! 3! !e,! 6E%@! T"!  V,! !f-! 640! %(#  $% 0#/

!: 7;!< &=!> .?!@ vA! !F #/001
 , F 07}}W)"002"113))&1FFvr   __main__N)
__future__r   r   r   r   __metaclass__DOCUMENTATIONEXAMPLESRETURNr   r   timer   copyr	   ansible.module_utils.basicr
   module_utils.apir   r   r   r   r   r   r6   r   r   <module>r      sr    A @BvpB
H )   4
M*3 M*`
6r zF r   