56 Commits

Author SHA1 Message Date
Алексей Даньков
b03b82e492 Merge branch 'fix/rc-5.2.2' into 'master'
Fix/rc 5.2.2

See merge request rudecs/decort-ansible!5
2022-10-31 08:44:40 +00:00
Алексей Даньков
15893f58bb Merge branch 'feature/decort_rg_refactoring' into 'master'
Feature/decort rg refactoring

See merge request rudecs/decort-ansible!4
2022-10-31 08:44:18 +00:00
msbolshakov
3681949ea6 decort_rg fix 2022-10-25 14:37:42 +07:00
msbolshakov
8713ebe099 decort_rg ifx 2022-10-21 23:27:40 +07:00
msbolshakov
87ecb762aa decort_rg ifx 2022-10-21 22:36:46 +07:00
msbolshakov
b30d8f2b3c decort_rg ifx 2022-10-21 21:53:57 +07:00
msbolshakov
36d6fe092d add exampels 2022-10-21 20:43:08 +07:00
msbolshakov
8f737397de decort_rg module refactoring 2022-10-21 18:47:13 +07:00
msbolshakov
72d9da0234 add account id in osimage facts 2022-10-11 21:11:38 +07:00
msbolshakov
4b3f34376b add account id in osimage facts 2022-10-11 20:18:07 +07:00
72b591723f decort_disk bug fix 2022-09-22 16:10:43 +00:00
f0e9ac10cc Bug fix 2022-09-21 15:53:02 +00:00
Alex_geth
82eef4492d Merge branch 'feature/decort_lb' 2022-09-21 13:00:55 +03:00
Alex_geth
889618f843 added LB module & some other fix 2022-09-20 21:44:32 +03:00
msbolshakov
ef33532a83 fix module errors 2022-09-19 17:56:45 +07:00
msbolshakov
8f7c933fb8 decort_disk remake 2022-09-16 23:26:11 +07:00
Alex_geth
dd28084b76 Merge branch 'rc-5.0-demo' of ssh://git.digitalenergy.online:2221/rudecs/decort-ansible into rc-5.0-demo 2022-09-02 14:57:06 +03:00
Alex_geth
8d6ed618ab decort_vins update & k8 wg fix 2022-09-02 14:56:58 +03:00
Maksim Bolshakov
e2c9f591b8 Add new file 2022-08-23 10:07:44 +03:00
Maksim Bolshakov
ebdf9aa012 Add new file 2022-08-23 10:07:18 +03:00
Maksim Bolshakov
31d6774475 Add new example 2022-08-23 10:06:23 +03:00
Maksim Bolshakov
6148e67dd1 Add new file 2022-08-02 15:02:18 +03:00
Maksim Bolshakov
4c7922cb55 Add new file 2022-08-02 15:01:45 +03:00
Maksim Bolshakov
8d51555db1 Add new file 2022-08-02 15:00:52 +03:00
Maksim Bolshakov
ebfb465531 Add new file 2022-08-02 15:00:02 +03:00
Maksim Bolshakov
f3504a3d50 Add new file 2022-07-18 16:59:23 +03:00
Alex_geth
1c3558d6f3 Merge branch 'rc-5.0-demo' of ssh://git.digitalenergy.online:2221/rudecs/decort-ansible into rc-5.0-demo 2022-07-18 14:24:25 +03:00
Alex_geth
12a80e7cf4 vins connect 2022-07-18 14:21:38 +03:00
Maksim Bolshakov
38d24cfa0a Update kubernetes.yaml 2022-07-12 17:17:40 +03:00
Maksim Bolshakov
ebe1c76a43 Update basicservices.yaml 2022-07-12 11:32:10 +03:00
msbolshakov
224ac59779 osimage fix 2022-07-11 16:05:07 +07:00
msbolshakov
ff4273cbce module_utils_fix 2022-07-11 15:59:04 +07:00
msbolshakov
5e5b6f6b8a osimage examples 2022-07-11 15:48:02 +07:00
msbolshakov
c497979efa osimage update 2022-07-11 15:43:53 +07:00
msbolshakov
28876ae38d decort_osimage update 2022-07-08 19:12:17 +07:00
Aleksandr Malyavin
ebe1a9194f change directory name example>examples 2022-06-29 18:26:58 +03:00
msbolshakov
d9ad1fee21 examples 2022-06-29 20:13:09 +07:00
Alex_geth
4b23cf8bae bservice module fix 2022-06-29 15:54:21 +03:00
Alex_geth
5b809dee4f basic services addon 2022-06-28 03:55:28 +03:00
Maksim Bolshakov
ac93e76005 Add new file 2022-05-27 18:15:53 +03:00
Maksim Bolshakov
aae60a46b9 Add new file 2022-05-27 18:15:21 +03:00
Maksim Bolshakov
25af1b4428 Add new file 2022-05-27 18:14:37 +03:00
Alex_geth
e058925f9b fix 2 2022-05-27 17:45:12 +03:00
Alex_geth
f6b0da976b k8s update 2022-05-27 07:42:48 +03:00
Maksim Bolshakov
9b9b92ff07 Update cloud_init-example 2022-05-25 17:01:13 +03:00
Maksim Bolshakov
498b46b4f2 New example for DECORT kvmvm module 2022-05-25 17:00:45 +03:00
Maksim Bolshakov
85da544614 Add new directory 2022-05-25 16:52:01 +03:00
Alex_geth
be7841ba38 fix 1 2022-05-25 10:56:44 +03:00
Alex_geth
aabd5dab6e demo update 2022-05-20 14:37:13 +03:00
Alex_geth
4a9d181782 pyJWT > 2.0.0 2022-04-01 14:50:47 +03:00
Aleksandr Malyavin
ec7d1fd181 Fix if else statement 2022-04-01 14:35:20 +03:00
Aleksandr Malyavin
4f151e174b Some changes 2022-04-01 12:01:44 +03:00
Aleksandr Malyavin
0237c469c5 Change API for get task request cloudbroker>cloudapi 2022-04-01 12:00:50 +03:00
Alex_geth
ab44d18d21 pyJWT > 2.0.0 2022-03-29 15:01:15 +03:00
Alex_geth
75bda6d76d update pyJWT > 2.0.0 2022-03-29 14:59:32 +03:00
Aleksandr Malyavin
41ca42dcab add k8s library 2022-03-28 19:53:34 +03:00
36 changed files with 707 additions and 694 deletions

0
examples/.gitkeep Normal file
View File

38
examples/cloud_init.yaml Normal file
View File

@@ -0,0 +1,38 @@
#
# DECORT kvmvm module example
#
- hosts: ansible_master
tasks:
- name: create a VM named cloud-init_example
decort_kvmvm:
annotation: "VM managed by decort_kvmvm module"
authenticator: oauth2
app_id: "" # Application id from SSO Digital Energy
app_secret: "" # API key from SSO Digital Energy
controller_url: "" #"https://mr4.digitalenergy.online"
name: cloud-init_example
cpu: 2
ram: 2048
boot_disk: 10
image_name: "DECS Ubuntu 18.04 v1.2.3" #Name of OS image
networks:
- type: VINS
id: #VINS id
tags: "Ansible cloud init example"
state: present
rg_id: #Resource group id
ci_user_data:
- packages:
- apache2
- write_files:
- content: |
<div>
Hello World!
</div>
owner: user:user
path: /var/www/html/index.html
- hostname: test-apache
- ssh_keys:
- rsa_public: ssh-rsa AAAAOasDmLxnD= user@pc
delegate_to: localhost
register: simple_vm

View File

@@ -0,0 +1,22 @@
---
- hosts: localhost
tasks:
- name: manage data disk 01
decort_disk:
authenticator: oauth2
app_id: #Application id from SSO DigitalEnergy
app_secret: #Application secret from SSO DigitalEnergy
controller_url: "https://cloud.digitalenergy.online"
account_name: "account_name"
name: "example_disk"
sep_id: 1
pool: 0
gid: 0
size: 2
type: "D"
description: "Disk created by decort_disk module"
iops: 2000
state: present
verify_ssl: false
delegate_to: localhost

View File

@@ -0,0 +1,18 @@
---
- hosts: localhost
tasks:
- name: manage data disk 01
decort_disk:
authenticator: oauth2
app_id: #Application id from SSO DigitalEnergy
app_secret: #Application secret from SSO DigitalEnergy
controller_url: "https://cloud.digitalenergy.online"
account_name: "account_name"
name: "example_disk"
permanently: False
force_detach: True
reason: "Just to test module decort_disk"
state: absent
verify_ssl: false
delegate_to: localhost

View File

@@ -0,0 +1,28 @@
---
- hosts: localhost
tasks:
- name: manage data disk 01
decort_disk:
authenticator: oauth2
app_id: #Application id from SSO DigitalEnergy
app_secret: #Application secret from SSO DigitalEnergy
controller_url: "https://cloud.digitalenergy.online"
account_name: "account_name"
id: 111
limitIO:
read_bytes_sec: 100
read_bytes_sec_max: 100
read_iops_sec: 100
read_iops_sec_max: 100
size_iops_sec: 100
write_bytes_sec: 100
write_bytes_sec_max: 100
write_iops_sec: 100
write_iops_sec_max: 100
total_bytes_sec: 0
total_iops_sec: 0
total_bytes_sec_max: 0
total_iops_sec_max: 0
verify_ssl: false
delegate_to: localhost

View File

@@ -0,0 +1,15 @@
---
- hosts: localhost
tasks:
- name: manage data disk 01
decort_disk:
authenticator: oauth2
app_id: #Application id from SSO DigitalEnergy
app_secret: #Application secret from SSO DigitalEnergy
controller_url: "https://cloud.digitalenergy.online"
account_name: "account_name"
id: 111
name: "example_disk2"
verify_ssl: false
delegate_to: localhost

View File

@@ -0,0 +1,15 @@
---
- hosts: localhost
tasks:
- name: manage data disk 01
decort_disk:
authenticator: oauth2
app_id: #Application id from SSO DigitalEnergy
app_secret: #Application secret from SSO DigitalEnergy
controller_url: "https://cloud.digitalenergy.online"
account_name: "account_name"
id: 111
state: present
verify_ssl: false
delegate_to: localhost

View File

@@ -0,0 +1,20 @@
- hosts: localhost
tasks:
- name: create
decort_rg:
authenticator: oauth2
controller_url: "https://cloud.digitalenergy.online"
rg_name: "rg_created_by_module"
# or
#rg_id: 999
account_id: 99
quotas:
cpu: 8
ram: 4096
disk: 20
ext_ips: 10
net_transfer: 200
state: present
verify_ssl: false
register: my_rg
delegate_to: localhost

View File

@@ -0,0 +1,21 @@
- hosts: localhost
tasks:
- name: create
decort_rg:
authenticator: oauth2
controller_url: "https://cloud.digitalenergy.online"
rg_name: "rg_created_by_module"
# or
#rg_id: 999
account_id: 99
resType:
- vins
- compute
- k8s
- openshift
- lb
- flipgroup
state: present
verify_ssl: false
register: my_rg
delegate_to: localhost

View File

@@ -0,0 +1,30 @@
- hosts: localhost
tasks:
- name: create
decort_rg:
authenticator: oauth2
controller_url: "https://cloud.digitalenergy.online"
rg_name: "rg_created_by_module"
account_id: 99
owner: "user_1" #Leave blank to set current user as owner.
quotas:
cpu: 8
ram: 4096
disk: 20
ext_ips: 10
net_transfer: 200
access:
action: "grant"
user: "user_2"
right: "RCX"
def_netType: "PRIVATE"
ipcidr: "" "192.168.1.1"
extNetId: 0
extNetIp: "" "10.100.1.10"
resType:
- vins
- compute
state: present
verify_ssl: false
register: my_rg
delegate_to: localhost

View File

@@ -0,0 +1,15 @@
- hosts: localhost
tasks:
- name: create
decort_rg:
authenticator: oauth2
controller_url: "https://cloud.digitalenergy.online"
rg_name: "test_rg"
# or
#rg_id: 999
account_id: 99
state: present
permanently: True
verify_ssl: false
register: my_rg
delegate_to: localhost

View File

@@ -0,0 +1,12 @@
- hosts: localhost
tasks:
- name: create
decort_rg:
authenticator: oauth2
controller_url: "https://cloud.digitalenergy.online"
rg_id: 999 # rg can be restored only by rg id
account_id: 99
state: present
verify_ssl: false
register: my_rg
delegate_to: localhost

View File

@@ -0,0 +1,14 @@
- hosts: localhost
tasks:
- name: create
decort_rg:
authenticator: oauth2
controller_url: "https://cloud.digitalenergy.online"
rg_name: "rg_created_by_module"
# or
#rg_id: 999
account_id: 99
state: enabled
verify_ssl: false
register: my_rg
delegate_to: localhost

View File

@@ -0,0 +1,18 @@
- hosts: localhost
tasks:
- name: create
decort_rg:
authenticator: oauth2
controller_url: "https://cloud.digitalenergy.online"
rg_name: "rg_created_by_module"
# or
#rg_id: 999
account_id: 99
access:
action: "grant"
user: "new_user"
right: "R"
state: present
verify_ssl: false
register: my_rg
delegate_to: localhost

View File

@@ -0,0 +1,15 @@
- hosts: localhost
tasks:
- name: create
decort_rg:
authenticator: oauth2
controller_url: "https://cloud.digitalenergy.online"
rg_name: "old_rg_name"
# or
#rg_id: 1737
account_id: 99
rename: "new_rg_name"
state: present
verify_ssl: false
register: my_rg
delegate_to: localhost

View File

@@ -0,0 +1,17 @@
- hosts: localhost
tasks:
- name: create
decort_rg:
authenticator: oauth2
controller_url: "https://cloud.digitalenergy.online"
rg_name: "rg_created_by_module"
# or
#rg_id: 999
account_id: 99
access:
action: "revoke"
user: "old_user"
state: present
verify_ssl: false
register: my_rg
delegate_to: localhost

View File

@@ -0,0 +1,16 @@
- hosts: localhost
tasks:
- name: create
decort_rg:
authenticator: oauth2
controller_url: "https://cloud.digitalenergy.online"
rg_name: "rg_created_by_module"
# or
#rg_id: 999
account_id: 99
def_netType: "PRIVATE"
def_netId: 199
state: present
verify_ssl: false
register: my_rg
delegate_to: localhost

View File

@@ -1,2 +0,0 @@
[all]
ansible_master ansible_host=<ansible host IP address> ansible_port=<SSH port on ansible host> ansible_user=root

View File

@@ -1,18 +0,0 @@
#
# More details on how to use DECORT Ansible module can be found at:
# https://github.com/rudecs/decort-ansible/wiki
#
- hosts: ansible_master
tasks:
- name: obtain JWT
decort_jwt:
oauth2_url: "{{ decort_sso }}" # "https://sso.digitalenergy.online"
validity: 1200
register: my_jwt
delegate_to: localhost
- name: print out JWT
debug:
var: my_jwt.jwt
delegate_to: localhost

View File

@@ -1,323 +0,0 @@
#
# More details on how to use DECORT Ansible module can be found at:
# https://github.com/rudecs/decort-ansible/wiki
#
- hosts: ansible_master
vars_files:
- vars.yaml
tasks:
- name: obtain JWT
decort_jwt:
oauth2_url: "{{ decort_sso }}"
validity: 1200
register: token
delegate_to: localhost
- name: obtain OS image
decort_osimage:
authenticator: jwt
jwt: "{{ token.jwt }}"
oauth2_url: "{{ decort_sso }}"
controller_url: "{{ decort_ctrl }}"
image_name: "{{ os_image_name }}"
account_name: "{{ target_account_name }}"
verify_ssl: false
register: my_image
delegate_to: localhost
- name: print out the result
debug:
var: my_image.facts
delegate_to: localhost
- name: manage RG
decort_rg:
authenticator: jwt
jwt: "{{ token.jwt }}"
oauth2_url: "{{ decort_sso }}"
controller_url: "{{ decort_ctrl }}"
account_id: 32
rg_name: "{{ target_rg_name }}"
state: present
verify_ssl: false
register: my_rg
delegate_to: localhost
- name: print out the result
debug:
var: my_rg.facts
delegate_to: localhost
- name: manage ViNS 01
decort_vins:
authenticator: jwt
jwt: "{{ token.jwt }}"
oauth2_url: "{{ decort_sso }}"
controller_url: "{{ decort_ctrl }}"
vins_name: "{{ vins01_name }}"
rg_id: "{{ my_rg.facts.id }}"
ext_net_id: "{{ target_ext_net_id }}"
state: present
verify_ssl: false
register: my_vins01
delegate_to: localhost
- name: print out the result
debug:
var: my_vins01.facts
delegate_to: localhost
- name: manage ViNS 02
decort_vins:
authenticator: jwt
jwt: "{{ token.jwt }}"
oauth2_url: "{{ decort_sso }}"
controller_url: "{{ decort_ctrl }}"
vins_name: "{{ vins02_name }}"
rg_id: "{{ my_rg.facts.id }}"
ext_net_id: -1
state: present
verify_ssl: false
register: my_vins02
delegate_to: localhost
- name: print out the result
debug:
var: my_vins02.facts
delegate_to: localhost
- name: manage data disk 01
decort_disk:
authenticator: jwt
jwt: "{{ token.jwt }}"
oauth2_url: "{{ decort_sso }}"
controller_url: "{{ decort_ctrl }}"
name: "{{ datadisk01_name }}"
size: "{{ datadisk01_size }}"
account_name: "{{ target_account_name }}"
pool: data01
place_with: "{{ my_image.facts.id }}"
state: present
verify_ssl: false
register: my_disk01
delegate_to: localhost
- name: print out the result
debug:
var: my_disk01.facts
delegate_to: localhost
- name: manage data disk 02
decort_disk:
authenticator: jwt
jwt: "{{ token.jwt }}"
oauth2_url: "{{ decort_sso }}"
controller_url: "{{ decort_ctrl }}"
name: "{{ datadisk02_name }}"
size: "{{ datadisk02_size }}"
account_name: "{{ target_account_name }}"
pool: data01
place_with: "{{ my_image.facts.id }}"
state: present
verify_ssl: false
register: my_disk02
delegate_to: localhost
- name: print out the result
debug:
var: my_disk02.facts
delegate_to: localhost
- name: manage KVM X86 VM
decort_kvmvm:
authenticator: jwt
jwt: "{{ token.jwt }}"
oauth2_url: "{{ decort_sso }}"
controller_url: "{{ decort_ctrl }}"
name: "{{ vm01_name }}"
arch: KVM_X86
ram: "{{ vm01_ram }}"
cpu: "{{ vm01_cpu }}"
image_id: "{{ my_image.facts.id }}"
boot_disk: "{{ vm01_boot_disk }}"
data_disks:
- "{{ my_disk01.facts.id }}"
- "{{ my_disk02.facts.id }}"
networks:
- type: VINS
id: "{{ my_vins01.facts.id }}"
- type: VINS
id: "{{ my_vins02.facts.id }}"
- type: EXTNET
id: "{{ target_ext_net_id }}"
rg_id: "{{ my_rg.facts.id }}"
state: present
verify_ssl: false
register: my_kvmvm
delegate_to: localhost
- name: print out the result
debug:
var: my_kvmvm.facts
delegate_to: localhost
- name: manage PFW rules on Compute
decort_pfw:
authenticator: jwt
jwt: "{{ token.jwt }}"
oauth2_url: "{{ decort_sso }}"
controller_url: "{{ decort_ctrl }}"
compute_id: "{{ my_kvmvm.facts.id }}"
vins_id: "{{ my_vins01.facts.id }}"
rules:
- public_port_start: 30022
local_port: 22
proto: tcp
- public_port_start: 30080
public_port_end: 30085
local_port: 30080
proto: tcp
state: present
verify_ssl: false
register: my_pfw
delegate_to: localhost
- name: print out the result
debug:
var: my_pfw.facts
delegate_to: localhost
- name: Create k8s cluster with params
decort_k8s:
authenticator: jwt
jwt: "{{ token.jwt }}"
controller_url: "{{ decort_ctrl }}"
k8s_name: "k8s_cluster_name"
wg_name: "k8s_wg_name"
k8ci_id: "{{ k8ci_id }}"
rg_id: "{{ my_rg.facts.id }}"
master_count: 1
master_cpu: 2
master_ram_mb: 2048
master_disk_gb: 20
worker_count: 3
worker_cpu: 1
worker_ram_mb: 1024
worker_disk_gb: 20
extnet_id: "{{ target_ext_net_id }}"
with_lb: True
state: present
register: k8s
delegate_to: localhost
- name: print out the result
debug:
var: k8s
delegate_to: localhost
- name: Disable k8s cluster
decort_k8s:
authenticator: jwt
jwt: "{{ token.jwt }}"
controller_url: "{{ decort_ctrl }}"
k8s_name: "k8s_cluster_name"
wg_name: "k8s_wg_name"
k8ci_id: "{{ k8ci_id }}"
rg_id: "{{ my_rg.facts.id }}"
state: disabled
register: k8s
delegate_to: localhost
- name: print out the result
debug:
var: k8s
delegate_to: localhost
- name: Delete in trash k8s cluster
decort_k8s:
authenticator: jwt
jwt: "{{ token.jwt }}"
controller_url: "{{ decort_ctrl }}"
k8s_name: "k8s_cluster_name"
wg_name: "k8s_wg_name"
k8ci_id: "{{ k8ci_id }}"
rg_id: "{{ my_rg.facts.id }}"
state: absent
permanent: False
register: k8s
delegate_to: localhost
- name: print out the result
debug:
var: k8s
delegate_to: localhost
- name: Restore from trash deleted k8s cluster
decort_k8s:
authenticator: jwt
jwt: "{{ token.jwt }}"
controller_url: "{{ decort_ctrl }}"
k8s_name: "k8s_cluster_name"
wg_name: "k8s_wg_name"
k8ci_id: "{{ k8ci_id }}"
rg_id: "{{ my_rg.facts.id }}"
state: enabled
register: k8s
delegate_to: localhost
- name: print out the result
debug:
var: k8s
delegate_to: localhost
- name: Enable k8s cluster
decort_k8s:
authenticator: jwt
jwt: "{{ token.jwt }}"
controller_url: "{{ decort_ctrl }}"
k8s_name: "k8s_cluster_name"
wg_name: "k8s_wg_name"
k8ci_id: "{{ k8ci_id }}"
rg_id: "{{ my_rg.facts.id }}"
state: enabled
register: k8s
delegate_to: localhost
- name: Enable k8s cluster
decort_k8s:
authenticator: jwt
jwt: "{{ token.jwt }}"
controller_url: "{{ decort_ctrl }}"
k8s_name: "k8s_cluster_name"
wg_name: "k8s_wg_name"
k8ci_id: "{{ k8ci_id }}"
rg_id: "{{ my_rg.facts.id }}"
state: enabled
started: True
register: k8s
delegate_to: localhost
- name: print out the result
debug:
var: k8s
delegate_to: localhost
- name: Destroy k8s cluster
decort_k8s:
authenticator: jwt
jwt: "{{ token.jwt }}"
controller_url: "{{ decort_ctrl }}"
k8s_name: "k8s_cluster_name"
wg_name: "k8s_wg_name"
k8ci_id: "{{ k8ci_id }}"
rg_id: "{{ my_rg.facts.id }}"
state: absent
permanent: True
register: k8s
delegate_to: localhost
- name: print out the result
debug:
var: k8s
delegate_to: localhost

View File

@@ -1,21 +0,0 @@
#!/bin/bash
# 1. As this file will contain sensitive data (application ID & Secret pair) put this file
# in a directory, where only you will have access to it, e.g. your ~/.ssh directory.
# 2. Make sure this file is not readable by anybody else (chmod 400).
# 3. Paste your Application ID (obtained from DECORT SSO application) to DECORT_APP_ID.
# 4. Paste your Application Secret (obtained from DECORT SSO application) to DECORT_APP_SECRET.
# 5. Paste DECORT SSO application URL to DECORT_OAUTH2_URL.
#
# Source this file into shell to prepare environment for running DECORT Ansible module, e.g.
# . ~/.ssh/prepenv.sh
#
# More informaiton on DECORT Ansible module can be found at:
# https://github.com/rudecs/decort-ansible/wiki
#
export DECORT_APP_ID="put your application ID here"
export DECORT_APP_SECRET="put your application secret here"
export DECORT_OAUTH2_URL="put DECORT SSO URL here" # "https://sso.digitalenergy.online"
export ANSIBLE_HOST_KEY_CHEKCING=False

View File

@@ -1,26 +0,0 @@
#
# More details on how to use DECORT Ansible module can be found at:
# https://github.com/rudecs/decort-ansible/wiki
#
decort_sso: "put DECORT SSO application URL here" # "https://sso.digitalenergy.online"
decort_ctrl: "put DECORT controller URL here" # "https://ds1.digitalenergy.online"
target_account_name: "your account name"
target_rg_name: "target resource group name"
os_image_name: "OS image name"
vins01_name: "Vins01-ansible"
vins02_name: "Vins02-ansible"
target_ext_net_id: 0
datadisk01_name: "Data01-ansible"
datadisk01_size: 5
datadisk02_name: "Data02-ansible"
datadisk02_size: 5
vm01_name: "Vm01-ansible"
vm01_cpu: 1
vm01_ram: 1024
vm01_boot_disk: 10

View File

@@ -424,6 +424,7 @@ class decort_osimage(DecortController):
ret_dict['pool'] = arg_osimage_facts['pool'] ret_dict['pool'] = arg_osimage_facts['pool']
ret_dict['state'] = arg_osimage_facts['status'] ret_dict['state'] = arg_osimage_facts['status']
ret_dict['linkto'] = arg_osimage_facts['linkTo'] ret_dict['linkto'] = arg_osimage_facts['linkTo']
ret_dict['accountId'] = arg_osimage_facts['accountId']
return ret_dict return ret_dict
@@ -521,9 +522,11 @@ def main():
decon.validated_image_id = decort_osimage.decort_osimage_package_facts(image_facts)['id'] decon.validated_image_id = decort_osimage.decort_osimage_package_facts(image_facts)['id']
elif amodule.params['state'] == "absent" and amodule.params['image_name'] or amodule.params['image_id'] and decort_osimage.decort_osimage_package_facts(image_facts)['accountId'] == amodule.params['account_Id']: elif amodule.params['state'] == "absent":
amodule.image_id_delete = decon.validated_image_id if amodule.params['image_name'] or amodule.params['image_id'] and\
decort_osimage.decort_image_delete(decon,amodule) decort_osimage.decort_osimage_package_facts(image_facts)['accountId'] == amodule.params['account_Id']:
amodule.image_id_delete = decon.validated_image_id
decort_osimage.decort_image_delete(decon,amodule)

View File

@@ -209,90 +209,253 @@ from ansible.module_utils.basic import env_fallback
from ansible.module_utils.decort_utils import * from ansible.module_utils.decort_utils import *
class decort_rg(DecortController):
def __init__(self,amodule):
super(decort_rg, self).__init__(amodule)
def decort_rg_package_facts(arg_rg_facts, arg_check_mode=False): self.validated_acc_id = 0
"""Package a dictionary of RG facts according to the decort_rg module specification. This dictionary will self.validated_rg_id = 0
be returned to the upstream Ansible engine at the completion of the module run. self.validated_rg_facts = None
@param arg_rg_facts: dictionary with RG facts as returned by API call to .../rg/get if self.amodule.params['account_id']:
@param arg_check_mode: boolean that tells if this Ansible module is run in check mode self.validated_acc_id, _ = self.account_find("", amodule.params['account_id'])
""" elif amodule.params['account_name']:
self.validated_acc_id, _ = self.account_find(amodule.params['account_name'])
if not self.validated_acc_id:
# we failed to locate account by either name or ID - abort with an error
self.result['failed'] = True
self.result['msg'] = ("Current user does not have access to the requested account "
"or non-existent account specified.")
self.fail_json(**self.result)
ret_dict = dict(id=0, if amodule.params['rg_id'] > 0:
name="none", self.validated_rg_id = amodule.params['rg_id']
state="CHECK_MODE",
) # Check if the RG with the specified parameters already exists
self.validated_rg_id, self.rg_facts = self.rg_find(self.validated_acc_id,
arg_rg_id = self.validated_rg_id,
arg_rg_name=amodule.params['rg_name'],
arg_check_state=False)
if amodule.params['state'] != "absent":
self.rg_should_exist = True
else:
self.rg_should_exist = False
def access(self):
should_change_access = False
acc_granted = False
for rg_item in self.rg_facts['acl']:
if rg_item['userGroupId'] == self.amodule.params['access']['user']:
acc_granted = True
if self.amodule.params['access']['action'] == 'grant':
if rg_item['right'] != self.amodule.params['access']['right']:
should_change_access = True
if self.amodule.params['access']['action'] == 'revoke':
should_change_access = True
if acc_granted == False and self.amodule.params['access']['action'] == 'grant':
should_change_access = True
if should_change_access == True:
self.rg_access(self.validated_rg_id, self.amodule.params['access'])
self.rg_facts['access'] = self.amodule.params['access']
self.rg_should_exist = True
return
def error(self):
self.result['failed'] = True
self.result['changed'] = False
if self.validated_rg_id > 0:
self.result['msg'] = ("Invalid target state '{}' requested for rg ID {} in the "
"current status '{}'.").format(self.validated_rg_id,
self.amodule.params['state'],
self.rg_facts['status'])
else:
self.result['msg'] = ("Invalid target state '{}' requested for non-existent rg name '{}' "
"in account ID {} ").format(self.amodule.params['state'],
self.amodule.params['rg_name'],
self.validated_acc_id)
return
def update(self):
resources = self.rg_facts['Resources']['Reserved']
incorrect_quota = dict(Requested=dict(),
Reserved=dict(),)
query_key_map = dict(cpu='cpu',
ram='ram',
disk='disksize',
ext_ips='extips',
net_transfer='exttraffic',)
if self.amodule.params['quotas']:
for quota_item in self.amodule.params['quotas']:
if self.amodule.params['quotas'][quota_item] < resources[query_key_map[quota_item]]:
incorrect_quota['Requested'][quota_item]=self.amodule.params['quotas'][quota_item]
incorrect_quota['Reserved'][quota_item]=resources[query_key_map[quota_item]]
if incorrect_quota['Requested']:
self.result['msg'] = ("Cannot limit less than already reserved'{}'").format(incorrect_quota)
self.result['failed'] = True
if self.result['failed'] != True:
self.rg_update(self.rg_facts, self.amodule.params['quotas'],
self.amodule.params['resType'], self.amodule.params['rename'])
self.rg_should_exist = True
return
def setDefNet(self):
if self.amodule.params['def_netId'] != self.rg_facts['def_net_id']:
self.rg_setDefNet(self.validated_rg_id,
self.amodule.params['def_netType'],
self.amodule.params['def_netId'])
self.rg_should_exist = True
return
def create(self):
self.validated_rg_id = self.rg_provision(self.validated_acc_id,
self.amodule.params['rg_name'],
self.amodule.params['owner'],
self.amodule.params['annotation'],
self.amodule.params['resType'],
self.amodule.params['def_netType'],
self.amodule.params['ipcidr'],
self.amodule.params['extNetId'],
self.amodule.params['extNetIp'],
self.amodule.params['quotas'],
"", # this is location code. TODO: add module argument
)
self.validated_rg_id, self.rg_facts = self.rg_find(self.validated_acc_id,
self.validated_rg_id,
arg_rg_name="",
arg_check_state=False)
self.rg_should_exist = True
return
def enable(self):
self.rg_enable(self.validated_rg_id,
self.amodule.params['state'])
if self.amodule.params['state'] == "enabled":
self.rg_facts['status'] = 'CREATED'
else:
self.rg_facts['status'] = 'DISABLED'
self.rg_should_exist = True
return
def restore(self):
self.rg_restore(self.validated_rg_id)
self.rg_facts['status'] = 'DISABLED'
self.rg_should_exist = True
return
def destroy(self):
self.rg_delete(self.validated_rg_id, self.amodule.params['permanently'])
if self.amodule.params['permanently'] == True:
self.rg_facts['status'] = 'DESTROYED'
else:
self.rg_facts['status'] = 'DELETED'
self.rg_should_exist = False
return
def package_facts(self, check_mode=False):
"""Package a dictionary of RG facts according to the decort_rg module specification. This dictionary will
be returned to the upstream Ansible engine at the completion of the module run.
@param arg_rg_facts: dictionary with RG facts as returned by API call to .../rg/get
@param arg_check_mode: boolean that tells if this Ansible module is run in check mode
"""
ret_dict = dict(id=0,
name="none",
state="CHECK_MODE",
)
if check_mode:
# in check mode return immediately with the default values
return ret_dict
#if arg_rg_facts is None:
# # if void facts provided - change state value to ABSENT and return
# ret_dict['state'] = "ABSENT"
# return ret_dict
ret_dict['id'] = self.rg_facts['id']
ret_dict['name'] = self.rg_facts['name']
ret_dict['state'] = self.rg_facts['status']
ret_dict['account_id'] = self.rg_facts['accountId']
ret_dict['gid'] = self.rg_facts['gid']
ret_dict['quota'] = self.rg_facts['resourceLimits']
ret_dict['resTypes'] = self.rg_facts['resourceTypes']
ret_dict['defNetId'] = self.rg_facts['def_net_id']
ret_dict['defNetType'] = self.rg_facts['def_net_type']
if arg_check_mode:
# in check mode return immediately with the default values
return ret_dict return ret_dict
if arg_rg_facts is None: def parameters():
# if void facts provided - change state value to ABSENT and return """Build and return a dictionary of parameters expected by decort_rg module in a form accepted
ret_dict['state'] = "ABSENT" by AnsibleModule utility class."""
return ret_dict
ret_dict['id'] = arg_rg_facts['id'] return dict(
ret_dict['name'] = arg_rg_facts['name'] account_id=dict(type='int', required=False),
ret_dict['state'] = arg_rg_facts['status'] account_name=dict(type='str', required=False, default=''),
ret_dict['account_id'] = arg_rg_facts['accountId'] access=dict(type='dict'),
ret_dict['gid'] = arg_rg_facts['gid'] annotation=dict(type='str', required=False, default=''),
app_id=dict(type='str',
return ret_dict
def decort_rg_parameters():
"""Build and return a dictionary of parameters expected by decort_rg module in a form accepted
by AnsibleModule utility class."""
return dict(
account_id=dict(type='int', required=False),
account_name=dict(type='str', required=False, default=''),
annotation=dict(type='str', required=False, default=''),
app_id=dict(type='str',
required=False,
fallback=(env_fallback, ['DECORT_APP_ID'])),
app_secret=dict(type='str',
required=False, required=False,
fallback=(env_fallback, ['DECORT_APP_SECRET']), fallback=(env_fallback, ['DECORT_APP_ID'])),
no_log=True), app_secret=dict(type='str',
authenticator=dict(type='str', required=False,
required=True, fallback=(env_fallback, ['DECORT_APP_SECRET']),
choices=['legacy', 'oauth2', 'jwt']), no_log=True),
controller_url=dict(type='str', required=True), authenticator=dict(type='str',
# datacenter=dict(type='str', required=False, default=''), required=True,
jwt=dict(type='str', choices=['legacy', 'oauth2', 'jwt']),
required=False, controller_url=dict(type='str', required=True),
fallback=(env_fallback, ['DECORT_JWT']), # datacenter=dict(type='str', required=False, default=''),
no_log=True), def_netType=dict(type='str', choices=['PRIVATE','PUBLIC', 'NONE'], default='PRIVATE'),
oauth2_url=dict(type='str', def_netId=dict(type='int', default=0),
required=False, extNetId=dict(type='int', default=0),
fallback=(env_fallback, ['DECORT_OAUTH2_URL'])), extNetIp=dict(type='str', default=""),
password=dict(type='str', owner=dict(type='str', default=""),
ipcidr=dict(type='str', default=""),
jwt=dict(type='str',
required=False,
fallback=(env_fallback, ['DECORT_JWT']),
no_log=True),
oauth2_url=dict(type='str',
required=False,
fallback=(env_fallback, ['DECORT_OAUTH2_URL'])),
rename=dict(type='str', default=""),
password=dict(type='str',
required=False,
fallback=(env_fallback, ['DECORT_PASSWORD']),
no_log=True),
quotas=dict(type='dict', required=False),
resType=dict(type='list'),
state=dict(type='str',
default='present',
choices=['absent', 'disabled', 'enabled', 'present']),
permanently=dict(type='bool',
default='False'),
user=dict(type='str',
required=False, required=False,
fallback=(env_fallback, ['DECORT_PASSWORD']), fallback=(env_fallback, ['DECORT_USER'])),
no_log=True), rg_name=dict(type='str', required=False,),
quotas=dict(type='dict', required=False), rg_id=dict(type='int', required=False, default=0),
state=dict(type='str', verify_ssl=dict(type='bool', required=False, default=True),
default='present', workflow_callback=dict(type='str', required=False),
choices=['absent', 'disabled', 'enabled', 'present']), workflow_context=dict(type='str', required=False),
user=dict(type='str', )
required=False,
fallback=(env_fallback, ['DECORT_USER'])),
rg_name=dict(type='str', required=True,),
verify_ssl=dict(type='bool', required=False, default=True),
workflow_callback=dict(type='str', required=False),
workflow_context=dict(type='str', required=False),
)
# Workflow digest: # Workflow digest:
# 1) authenticate to DECORT controller & validate authentication by issuing API call - done when creating DECORTController # 1) authenticate to DECORT controller & validate authentication by issuing API call - done when creating DECORTController
# 2) check if the RG with the specified id or rg_name:name exists # 2) check if the RG with the specified id or rg_name:name exists
# 3) if RG does not exist -> deploy # 3) if RG does not exist -> deploy
# 4) if RG exists: check desired state, desired configuration -> initiate action accordingly # 4) if RG exists: check desired state, desired configuration -> initiate action accordingly
# 5) report result to Ansible # 5) report result to Ansible
def main(): def main():
module_parameters = decort_rg_parameters() module_parameters = decort_rg.parameters()
amodule = AnsibleModule(argument_spec=module_parameters, amodule = AnsibleModule(argument_spec=module_parameters,
supports_check_mode=True, supports_check_mode=True,
@@ -307,157 +470,52 @@ def main():
], ],
) )
decon = DecortController(amodule) decon = decort_rg(amodule)
#amodule.check_mode=True
# We need valid Account ID to manage RG. if decon.validated_rg_id > 0:
# Account may be specified either by account_id or account_name. In both cases we if decon.rg_facts['status'] in ["MODELED", "DISABLING", "ENABLING", "DELETING", "DESTROYING", "CONFIRMED"]:
# have to validate account presence and accesibility by the current user. decon.error()
validated_acc_id = 0 elif decon.rg_facts['status'] in ("CREATED"):
if decon.check_amodule_argument('account_id', False):
validated_acc_id, _ = decon.account_find("", amodule.params['account_id'])
else:
decon.check_amodule_argument('account_name') # if no account_name, this function will abort module
validated_acc_id, _ = decon.account_find(amodule.params['account_name'])
if not validated_acc_id:
# we failed to locate account by either name or ID - abort with an error
decon.result['failed'] = True
decon.result['msg'] = ("Current user does not have access to the requested account "
"or non-existent account specified.")
decon.fail_json(**decon.result)
# Check if the RG with the specified parameters already exists
rg_id, rg_facts = decon.rg_find(validated_acc_id,
0, arg_rg_name=amodule.params['rg_name'],
arg_check_state=False)
rg_should_exist = True
if rg_id:
if rg_facts['status'] in ["MODELED", "DISABLING", "ENABLING", "DELETING", "DESTROYING"]:
# error: nothing can be done to existing RG in the listed statii regardless of
# the requested state
decon.result['failed'] = True
decon.result['changed'] = False
decon.result['msg'] = ("No change can be done for existing RG ID {} because of its current "
"status '{}'").format(rg_id, rg_facts['status'])
elif rg_facts['status'] == "DISABLED":
if amodule.params['state'] == 'absent': if amodule.params['state'] == 'absent':
decon.rg_delete(arg_rg_id=rg_id, arg_permanently=True) decon.destroy()
rg_facts['status'] = 'DESTROYED' elif amodule.params['state'] == "disabled":
rg_should_exist = False decon.enable()
elif amodule.params['state'] in ('present', 'disabled'):
# update quotas
decon.rg_quotas(rg_facts, amodule.params['quotas'])
elif amodule.params['state'] == 'enabled':
# update quotas and enable
decon.rg_quotas(rg_facts, amodule.params['quotas'])
decon.rg_state(rg_facts, 'enabled')
elif rg_facts['status'] == "CREATED":
if amodule.params['state'] == 'absent':
decon.rg_delete(arg_rg_id=rg_id, arg_permanently=True)
rg_facts['status'] = 'DESTROYED'
rg_should_exist = False
elif amodule.params['state'] in ('present', 'enabled'):
# update quotas
decon.rg_quotas(rg_facts, amodule.params['quotas'])
elif amodule.params['state'] == 'disabled':
# disable and update quotas
decon.rg_state(rg_facts, 'disabled')
decon.rg_quotas(rg_facts, amodule.params['quotas'])
elif rg_facts['status'] == "DELETED":
if amodule.params['state'] in ['present', 'enabled']: if amodule.params['state'] in ['present', 'enabled']:
# restore and enable if amodule.params['quotas'] or amodule.params['resType'] or amodule.params['rename'] != "":
# TODO: check if restore RG API returns the new RG ID of the restored RG instance. decon.update()
decon.rg_restore(arg_rg_id=rg_id) if amodule.params['access']:
decon.rg_state(rg_facts, 'enabled') decon.access()
# TODO: Not sure what to do with the quotas after RG is restored. May need to update rg_facts. if amodule.params['def_netId'] > 0:
rg_should_exist = True decon.setDefNet()
elif amodule.params['state'] == 'absent':
# destroy permanently elif decon.rg_facts['status'] == "DELETED":
decon.rg_delete(arg_rg_id=rg_id, arg_permanently=True) if amodule.params['state'] == 'absent' and amodule.params['permanently'] == True:
rg_facts['status'] = 'DESTROYED' decon.destroy()
rg_should_exist = False elif amodule.params['state'] == 'present':
elif amodule.params['state'] == 'disabled': decon.restore()
# error elif decon.rg_facts['status'] in ("DISABLED"):
decon.result['failed'] = True if amodule.params['state'] == 'absent':
decon.result['changed'] = False decon.destroy()
decon.result['msg'] = ("Invalid target state '{}' requested for RG ID {} in the " elif amodule.params['state'] == ("enabled"):
"current status '{}'").format(rg_id, decon.enable()
amodule.params['state'],
rg_facts['status'])
rg_should_exist = False
elif rg_facts['status'] == "DESTROYED":
if amodule.params['state'] in ('present', 'enabled'):
# need to re-provision RG
decon.check_amodule_argument('rg_name')
# As we already have validated account ID we can create RG and get rg_id on success
# pass empty string for location code, rg_provision will select the 1st location
rg_id = decon.rg_provision(validated_acc_id,
amodule.params['rg_name'], decon.decort_username,
amodule.params['quotas'],
"", # this is location code. TODO: add module argument
amodule.params['annotation'])
rg_should_exist = True
elif amodule.params['state'] == 'absent':
# nop
decon.result['failed'] = False
decon.result['changed'] = False
decon.result['msg'] = ("No state change required for RG ID {} because of its "
"current status '{}'").format(rg_id,
rg_facts['status'])
rg_should_exist = False
elif amodule.params['state'] == 'disabled':
# error
decon.result['failed'] = True
decon.result['changed'] = False
decon.result['msg'] = ("Invalid target state '{}' requested for RG ID {} in the "
"current status '{}'").format(rg_id,
amodule.params['state'],
rg_facts['status'])
else: else:
# Preexisting RG was not found. if amodule.params['state'] in ('present', 'enabled'):
rg_should_exist = False # we will change it back to True if RG is explicitly created or restored decon.create()
# If requested state is 'absent' - nothing to do if amodule.params['access']:
if amodule.params['state'] == 'absent': decon.access()
decon.result['failed'] = False elif amodule.params['state'] in ('disabled'):
decon.result['changed'] = False decon.error()
decon.result['msg'] = ("Nothing to do as target state 'absent' was requested for "
"non-existent RG name '{}'").format(amodule.params['rg_name'])
elif amodule.params['state'] in ('present', 'enabled'):
# Target RG does not exist yet - create it and store the returned ID in rg_id variable for later use
# To create RG we need account name (or account ID) and RG name - check
# that these parameters are present and proceed.
decon.check_amodule_argument('rg_name')
# as we already have account ID we can create RG and get rg_id on success
# pass empty string for location code, rg_provision will select the 1st location
rg_id = decon.rg_provision(validated_acc_id,
amodule.params['rg_name'], decon.decort_username,
amodule.params['quotas'],
"", # this is location code. TODO: add module argument
amodule.params['annotation'])
rg_should_exist = True
elif amodule.params['state'] == 'disabled':
decon.result['failed'] = True
decon.result['changed'] = False
decon.result['msg'] = ("Invalid target state '{}' requested for non-existent "
"RG name '{}' ").format(amodule.params['state'],
amodule.params['rg_name'])
#
# conditional switch end - complete module run
if decon.result['failed']: if decon.result['failed']:
amodule.fail_json(**decon.result) amodule.fail_json(**decon.result)
else: else:
# prepare RG facts to be returned as part of decon.result and then call exit_json(...) if decon.rg_should_exist:
# rg_facts = None decon.result['facts'] = decon.package_facts(amodule.check_mode)
if rg_should_exist: amodule.exit_json(**decon.result)
if decon.result['changed']: else:
# If we arrive here, there is a good chance that the RG is present - get fresh RG facts from amodule.exit_json(**decon.result)
# the cloud by RG ID.
# Otherwise, RG facts from previous call (when the RG was still in existence) will be returned.
_, rg_facts = decon.rg_find(arg_account_id=0, arg_rg_id=rg_id)
decon.result['facts'] = decort_rg_package_facts(rg_facts, amodule.check_mode)
amodule.exit_json(**decon.result)
if __name__ == "__main__": if __name__ == "__main__":
main() main()

View File

@@ -1442,7 +1442,7 @@ class DecortController(object):
################################### ###################################
# Resource Group (RG) manipulation methods # Resource Group (RG) manipulation methods
################################### ###################################
def rg_delete(self, rg_id, permanently=False): def rg_delete(self, rg_id, permanently):
"""Deletes specified VDC. """Deletes specified VDC.
@param (int) rg_id: integer value that identifies the RG to be deleted. @param (int) rg_id: integer value that identifies the RG to be deleted.
@@ -1500,7 +1500,27 @@ class DecortController(object):
return ret_rg_id, ret_rg_dict return ret_rg_id, ret_rg_dict
def rg_find(self, arg_account_id, arg_rg_id=0, arg_rg_name="", arg_check_state=True): def rg_access(self, arg_rg_id, arg_access):
self.result['waypoints'] = "{} -> {}".format(self.result['waypoints'], "rg_access")
if self.amodule.check_mode:
self.result['failed'] = False
self.result['msg'] = ("rg_access() in check mode: access to RG id '{}' was "
"requested with '{}'.").format(arg_rg_id, arg_access)
return 0
api_params=dict(rgId=arg_rg_id,)
if arg_access['action'] == "grant":
api_params['user']=arg_access['user'],
api_params['right']=arg_access['right'],
api_resp = self.decort_api_call(requests.post, "/restmachine/cloudapi/rg/accessGrant", api_params)
else:
api_params['user']=arg_access['user'],
api_resp = self.decort_api_call(requests.post, "/restmachine/cloudapi/rg/accessRevoke", api_params)
self.result['changed'] = True
return
def rg_find(self, arg_account_id, arg_rg_id, arg_rg_name="", arg_check_state=True):
"""Returns non zero RG ID and a dictionary with RG details on success, 0 and empty dictionary otherwise. """Returns non zero RG ID and a dictionary with RG details on success, 0 and empty dictionary otherwise.
This method does not fail the run if RG cannot be located by its name (arg_rg_name), because this could be This method does not fail the run if RG cannot be located by its name (arg_rg_name), because this could be
an indicator of the requested RG never existed before. an indicator of the requested RG never existed before.
@@ -1529,7 +1549,7 @@ class DecortController(object):
self.result['waypoints'] = "{} -> {}".format(self.result['waypoints'], "rg_find") self.result['waypoints'] = "{} -> {}".format(self.result['waypoints'], "rg_find")
ret_rg_id = 0 ret_rg_id = arg_rg_id
api_params = dict() api_params = dict()
ret_rg_dict = None ret_rg_dict = None
@@ -1574,7 +1594,44 @@ class DecortController(object):
return ret_rg_id, ret_rg_dict return ret_rg_id, ret_rg_dict
def rg_provision(self, arg_account_id, arg_rg_name, arg_username, arg_quota={}, arg_location="", arg_desc=""): def rg_setDefNet(self, arg_rg_id, arg_net_type, arg_net_id):
self.result['waypoints'] = "{} -> {}".format(self.result['waypoints'], "rg_setDefNet")
if self.amodule.check_mode:
self.result['failed'] = False
self.result['msg'] = ("rg_setDefNet() in check mode: setDefNet RG id '{}' was "
"requested.").format(arg_rg_id)
return 0
if arg_net_type == "NONE":
arg_net_type = "PRIVATE"
api_params = dict(rgId=arg_rg_id,
netType=arg_net_type,
netId=arg_net_id,)
api_resp = self.decort_api_call(requests.post, "/restmachine/cloudapi/rg/setDefNet", api_params)
self.result['changed'] = True
return
def rg_enable(self, arg_rg_id, arg_state):
self.result['waypoints'] = "{} -> {}".format(self.result['waypoints'], "rg_enable")
if self.amodule.check_mode:
self.result['failed'] = False
self.result['msg'] = ("rg_enable() in check mode: '{}' RG id '{}' was "
"requested.").format(arg_state, arg_rg_id)
return 0
api_params = dict(rgId=arg_rg_id)
if arg_state == "enabled":
api_resp = self.decort_api_call(requests.post, "/restmachine/cloudapi/rg/enable", api_params)
else:
api_resp = self.decort_api_call(requests.post, "/restmachine/cloudapi/rg/disable", api_params)
self.result['changed'] = True
return
def rg_provision(self, arg_account_id, arg_rg_name, arg_username, arg_desc, restype, arg_net_type, ipcidr, arg_extNetId, arg_extIp, arg_quota={}, location=""):
"""Provision new RG according to the specified arguments. """Provision new RG according to the specified arguments.
If critical error occurs the embedded call to API function will abort further execution of the script If critical error occurs the embedded call to API function will abort further execution of the script
and relay error to Ansible. and relay error to Ansible.
@@ -1587,7 +1644,7 @@ class DecortController(object):
access to the newly created RG. access to the newly created RG.
@param arg_quota: dictionary that defines quotas to set on the RG to be created. Valid keys are: cpu, ram, @param arg_quota: dictionary that defines quotas to set on the RG to be created. Valid keys are: cpu, ram,
disk and ext_ips. disk and ext_ips.
@param (string) arg_location: location code, which identifies the location where RG will be created. If @param (string) location: location code, which identifies the location where RG will be created. If
empty string is passed, the first location under current DECORT controller will be selected. empty string is passed, the first location under current DECORT controller will be selected.
@param (string) arg_desc: optional text description of this resource group. @param (string) arg_desc: optional text description of this resource group.
@@ -1602,20 +1659,23 @@ class DecortController(object):
"requested.").format(arg_rg_name) "requested.").format(arg_rg_name)
return 0 return 0
target_gid = self.gid_get(arg_location) target_gid = self.gid_get(location)
if not target_gid: if not target_gid:
self.result['failed'] = True self.result['failed'] = True
self.result['msg'] = ("rg_provision() failed to obtain valid Grid ID for location '{}'").format( self.result['msg'] = ("rg_provision() failed to obtain valid Grid ID for location '{}'").format(
arg_location) location)
self.amodule.fail_json(**self.result) self.amodule.fail_json(**self.result)
api_params = dict(accountId=arg_account_id, api_params = dict(accountId=arg_account_id,
gid=target_gid, gid=target_gid,
name=arg_rg_name, name=arg_rg_name,
owner=arg_username, owner=arg_username,
def_net="NONE", def_net=arg_net_type,
extNetId=arg_extNetId,
extIp=arg_extIp,
# maxMemoryCapacity=-1, maxVDiskCapacity=-1, # maxMemoryCapacity=-1, maxVDiskCapacity=-1,
# maxCPUCapacity=-1, maxNumPublicIP=-1, # maxCPUCapacity=-1, maxNumPublicIP=-1,
# maxNetworkPeerTransfer=-1,
) )
if arg_quota: if arg_quota:
if 'ram' in arg_quota: if 'ram' in arg_quota:
@@ -1626,9 +1686,20 @@ class DecortController(object):
api_params['maxCPUCapacity'] = arg_quota['cpu'] api_params['maxCPUCapacity'] = arg_quota['cpu']
if 'ext_ips' in arg_quota: if 'ext_ips' in arg_quota:
api_params['maxNumPublicIP'] = arg_quota['ext_ips'] api_params['maxNumPublicIP'] = arg_quota['ext_ips']
if 'net_transfer' in arg_quota:
api_params['maxNetworkPeerTransfer'] = arg_quota['net_transfer']
if restype:
api_params['resourceTypes'] = restype
if arg_desc: if arg_desc:
api_params['desc'] = arg_desc api_params['desc'] = arg_desc
api_params['def_net'] = arg_net_type
if arg_net_type == "PRIVATE" and ipcidr !="":
api_params['ipcidr'] = ipcidr
api_resp = self.decort_api_call(requests.post, "/restmachine/cloudapi/rg/create", api_params) api_resp = self.decort_api_call(requests.post, "/restmachine/cloudapi/rg/create", api_params)
# On success the above call will return here. On error it will abort execution by calling fail_json. # On success the above call will return here. On error it will abort execution by calling fail_json.
@@ -1639,7 +1710,8 @@ class DecortController(object):
return ret_rg_id return ret_rg_id
# TODO: this method will not work in its current implementation. Update it for new .../rg/update specs. # TODO: this method will not work in its current implementation. Update it for new .../rg/update specs.
def rg_quotas(self, arg_rg_dict, arg_quotas):
def rg_update(self, arg_rg_dict, arg_quotas, arg_res_types, arg_newname):
"""Manage quotas for an existing RG. """Manage quotas for an existing RG.
@param arg_rg_dict: dictionary with RG facts as returned by rg_find(...) method or .../rg/get API @param arg_rg_dict: dictionary with RG facts as returned by rg_find(...) method or .../rg/get API
@@ -1653,46 +1725,59 @@ class DecortController(object):
# TODO: this method may need update since we introduced GPU functionality and corresponding GPU quota management. # TODO: this method may need update since we introduced GPU functionality and corresponding GPU quota management.
# #
self.result['waypoints'] = "{} -> {}".format(self.result['waypoints'], "rg_quotas") self.result['waypoints'] = "{} -> {}".format(self.result['waypoints'], "rg_update")
if self.amodule.check_mode: if self.amodule.check_mode:
self.result['failed'] = False self.result['failed'] = False
self.result['msg'] = ("rg_quotas() in check mode: setting quotas on RG ID {}, RG name '{}' was " self.result['msg'] = ("rg_update() in check mode: setting quotas on RG ID {}, RG name '{}' was "
"requested.").format(arg_rg_dict['id'], arg_rg_dict['name']) "requested.").format(arg_rg_dict['id'], arg_rg_dict['name'])
return return
update_required = False
api_params = dict(rgId=arg_rg_dict['id'],)
if arg_res_types:
if arg_rg_dict['resourceTypes'] != arg_res_types:
api_params['resourceTypes'] = arg_res_types
update_required = True
else:
api_params['resourceTypes'] = arg_rg_dict['resourceTypes']
if arg_newname != "" and arg_newname!=arg_rg_dict['name']:
api_params['name'] = arg_newname
update_required = True
# One more inconsistency in API keys: # One more inconsistency in API keys:
# - when setting resource limits, the keys are in the form 'max{{ RESOURCE_NAME }}Capacity' # - when setting resource limits, the keys are in the form 'max{{ RESOURCE_NAME }}Capacity'
# - when quering resource limits, the keys are in the form of cloud units (CU_*) # - when quering resource limits, the keys are in the form of cloud units (CU_*)
query_key_map = dict(cpu='CU_C', query_key_map = dict(cpu='CU_C',
ram='CU_M', ram='CU_M',
disk='CU_D', disk='CU_D',
ext_ips='CU_I', ) ext_ips='CU_I',
net_transfer='CU_NP',)
set_key_map = dict(cpu='maxCPUCapacity', set_key_map = dict(cpu='maxCPUCapacity',
ram='maxMemoryCapacity', ram='maxMemoryCapacity',
disk='maxVDiskCapacity', disk='maxVDiskCapacity',
ext_ips='maxNumPublicIP', ) ext_ips='maxNumPublicIP',
api_params = dict(rgId=arg_rg_dict['id'], ) net_transfer='maxNetworkPeerTransfer',)
quota_change_required = False
for new_limit in ('cpu', 'ram', 'disk', 'ext_ips'): for new_limit in ('cpu', 'ram', 'disk', 'ext_ips', 'net_transfer'):
if arg_quotas: if arg_quotas:
if new_limit in arg_quotas: if new_limit in arg_quotas:
# If this resource type limit is found in the desired quotas, check if the desired setting is # If this resource type limit is found in the desired quotas, check if the desired setting is
# different from the current settings of VDC. If it is different, set the new one. # different from the current settings of VDC. If it is different, set the new one.
if arg_quotas[new_limit] != arg_rg_dict['resourceLimits'][query_key_map[new_limit]]: if arg_quotas[new_limit] != arg_rg_dict['resourceLimits'][query_key_map[new_limit]]:
api_params[set_key_map[new_limit]] = arg_quotas[new_limit] api_params[set_key_map[new_limit]] = arg_quotas[new_limit]
quota_change_required = True update_required = True
elif arg_quotas[new_limit] == arg_rg_dict['resourceLimits'][query_key_map[new_limit]]:
api_params[set_key_map[new_limit]] = arg_quotas[new_limit]
else: else:
# This resource type limit not found in the desired quotas. It means that no limit for this api_params[set_key_map[new_limit]] = arg_rg_dict['resourceLimits'][query_key_map[new_limit]]
# resource type - reset VDC limit for this resource type regardless of the current VDC settings.
api_params[set_key_map[new_limit]] = -1
# quota_change_required = True
else: else:
# if quotas dictionary is None, it means that no quotas should be set - reset the limits # if quotas dictionary is None, it means that no quotas should be set - reset the limits
api_params[set_key_map[new_limit]] = -1 api_params[set_key_map[new_limit]] = arg_rg_dict['resourceLimits'][query_key_map[new_limit]]
if quota_change_required: if update_required:
self.decort_api_call(requests.post, "/restmachine/cloudapi/rg/update", api_params) self.decort_api_call(requests.post, "/restmachine/cloudapi/rg/update", api_params)
# On success the above call will return here. On error it will abort execution by calling fail_json. # On success the above call will return here. On error it will abort execution by calling fail_json.
self.result['failed'] = False self.result['failed'] = False
@@ -1722,66 +1807,6 @@ class DecortController(object):
self.result['changed'] = True self.result['changed'] = True
return return
def rg_state(self, arg_rg_dict, arg_desired_state):
"""Enable or disable RG.
@param arg_rg_dict: dictionary with the target RG facts as returned by rg_find(...) method or
.../rg/get API call.
@param arg_desired_state: the desired state for this RG. Valid states are 'enabled' and 'disabled'.
"""
self.result['waypoints'] = "{} -> {}".format(self.result['waypoints'], "rg_state")
NOP_STATES_FOR_RG_CHANGE = ["MODELED", "DISABLING", "ENABLING", "DELETING", "DELETED", "DESTROYING",
"DESTROYED"]
VALID_TARGET_STATES = ["enabled", "disabled"]
if arg_rg_dict['status'] in NOP_STATES_FOR_RG_CHANGE:
self.result['failed'] = False
self.result['msg'] = ("rg_state(): no state change possible for RG ID {} "
"in its current state '{}'.").format(arg_rg_dict['id'], arg_rg_dict['status'])
return
if arg_desired_state not in VALID_TARGET_STATES:
self.result['failed'] = False
self.result['warning'] = ("rg_state(): unrecognized desired state '{}' requested "
"for RG ID {}. No RG state change will be done.").format(arg_desired_state,
arg_rg_dict['id'])
return
if self.amodule.check_mode:
self.result['failed'] = False
self.result['msg'] = ("rg_state() in check mode: setting state of RG ID {}, name '{}' to "
"'{}' was requested.").format(arg_rg_dict['id'], arg_rg_dict['name'],
arg_desired_state)
return
rgstate_api = "" # this string will also be used as a flag to indicate that API call is necessary
api_params = dict(rgId=arg_rg_dict['id'],
reason='Changed by DECORT Ansible module, rg_state method.')
expected_state = ""
if arg_rg_dict['status'] in ["CREATED", "ENABLED"] and arg_desired_state == 'disabled':
rgstate_api = "/restmachine/cloudapi/rg/disable"
expected_state = "DISABLED"
elif arg_rg_dict['status'] == "DISABLED" and arg_desired_state == 'enabled':
rgstate_api = "/restmachine/cloudapi/rg/enable"
expected_state = "ENABLED"
if rgstate_api != "":
self.decort_api_call(requests.post, rgstate_api, api_params)
# On success the above call will return here. On error it will abort execution by calling fail_json.
self.result['failed'] = False
self.result['changed'] = True
arg_rg_dict['status'] = expected_state
else:
self.result['failed'] = False
self.result['msg'] = ("rg_state(): no state change required for RG ID {} from current "
"state '{}' to desired state '{}'.").format(arg_rg_dict['id'],
arg_rg_dict['status'],
arg_desired_state)
return
def account_find(self, account_name, account_id=0): def account_find(self, account_name, account_id=0):
"""Find cloud account specified by the name and return facts about the account. Knowing account is """Find cloud account specified by the name and return facts about the account. Knowing account is
required for certain cloud resource management tasks (e.g. creating new RG). required for certain cloud resource management tasks (e.g. creating new RG).
@@ -1971,10 +1996,13 @@ class DecortController(object):
ret_gid = 0 ret_gid = 0
api_params = dict() api_params = dict()
api_resp = self.decort_api_call(requests.post, "/restmachine/cloudapi/locations/list", api_params) api_resp = self.decort_api_call(requests.post, "/restmachine/cloudapi/locations/list", api_params)
if api_resp.status_code == 200: if api_resp.status_code == 200:
locations = json.loads(api_resp.content.decode('utf8')) locations = json.loads(api_resp.content.decode('utf8'))
if location_code == "" and locations: if location_code == "" and locations:
ret_gid = locations['gid'] ret_gid = locations[0]['gid']
else: else:
for runner in locations: for runner in locations:
if runner['locationCode'] == location_code: if runner['locationCode'] == location_code: