New cloud provider CloudStack (#1420)

* clean commits from branch cloud-cloudstack w/ proper committer email/name

* fixed ansible-lint errors

* corrected typo in prompted message

* standalone cloudstack zones module

* added missing environment variables

* remove `_cloudstack_zones` default variable

* Move to Ubuntu 19.04

* Update cloud-cloudstack.md

* Update cloud-cloudstack.md

Markdown doesn't render `<your account>`

* Update prompts.yml

* Update main.yml
pull/1553/head
Julien Bachmann 5 years ago committed by Jack Ivanov
parent 2909107554
commit 3dc08c94cf

@ -160,6 +160,10 @@ cloud_providers:
openstack:
flavor_ram: ">=512"
image: Ubuntu-18.04
cloudstack:
size: Micro
image: Linux Ubuntu 19.04 64-bit
disk: 10
vultr:
os: Ubuntu 19.04 x64
size: 1024 MB RAM,25 GB SSD,1.00 TB BW

@ -0,0 +1,20 @@
### Configuration file
You need to create a configuration file in INI format with your api key in `$HOME/.cloudstack.ini`
```
[cloudstack]
endpoint = <endpoint>
key = <your api key>
secret = <your secret>
timeout = 30
```
Example for Exoscale (European cloud provider exposing CloudStack API), visit https://portal.exoscale.com/u/<your@account>/account/profile/api to gather the required information:
```
[exoscale]
endpoint = https://api.exoscale.com/compute
key = <your api key>
secret = <your secret>
timeout = 30
```

@ -20,6 +20,7 @@
- Configure [DigitalOcean](cloud-do.md)
- Configure [Google Cloud Platform](cloud-gce.md)
- Configure [Vultr](cloud-vultr.md)
- Configure [CloudStack](cloud-cloudstack.md)
* Advanced Deployment
- Deploy to your own [FreeBSD](deploy-to-freebsd.md) server
- Deploy to your own [Ubuntu](deploy-to-ubuntu.md) server

@ -19,6 +19,7 @@
- { name: Google Compute Engine, alias: gce }
- { name: Scaleway, alias: scaleway}
- { name: OpenStack (DreamCompute optimised), alias: openstack }
- { name: CloudStack (Exoscale optimised), alias: cloudstack }
- { name: Install to existing Ubuntu 18.04 or 19.04 server (Advanced), alias: local }
vars_files:
- config.cfg

@ -0,0 +1,110 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
#
ANSIBLE_METADATA = {'metadata_version': '1.1',
'status': ['preview'],
'supported_by': 'community'}
from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils.cloudstack import (
AnsibleCloudStack,
cs_argument_spec,
cs_required_together,
)
DOCUMENTATION = '''
---
module: cloudstack_zones
short_description: List zones on Apache CloudStack based clouds.
description:
- List zones.
version_added: '0.1'
author: Julien Bachmann (@0xmilkmix)
extends_documentation_fragment: cloudstack
'''
EXAMPLES = '''
- name: List zones
cloudstack_zones:
register: _cs_zones
'''
RETURN = '''
---
zone:
description: List of zones.
returned: success
type: list
sample:
[
{
"allocationstate": "Enabled",
"dhcpprovider": "VirtualRouter",
"id": "<id>",
"localstorageenabled": true,
"name": "ch-gva-2",
"networktype": "Basic",
"securitygroupsenabled": true,
"tags": [],
"zonetoken": "token"
},
{
"allocationstate": "Enabled",
"dhcpprovider": "VirtualRouter",
"id": "<id>",
"localstorageenabled": true,
"name": "ch-dk-2",
"networktype": "Basic",
"securitygroupsenabled": true,
"tags": [],
"zonetoken": "token"
},
{
"allocationstate": "Enabled",
"dhcpprovider": "VirtualRouter",
"id": "<id>",
"localstorageenabled": true,
"name": "at-vie-1",
"networktype": "Basic",
"securitygroupsenabled": true,
"tags": [],
"zonetoken": "token"
},
{
"allocationstate": "Enabled",
"dhcpprovider": "VirtualRouter",
"id": "<id>",
"localstorageenabled": true,
"name": "de-fra-1",
"networktype": "Basic",
"securitygroupsenabled": true,
"tags": [],
"zonetoken": "token"
}
]
'''
class AnsibleCloudStackZones(AnsibleCloudStack):
def __init__(self, module):
super(AnsibleCloudStackZones, self).__init__(module)
self.zones = None
def get_zones(self):
args = {}
if not self.zones:
zones = self.query_api('listZones', **args)
if zones:
self.zones = zones
return self.zones
def main():
module = AnsibleModule(argument_spec={})
acs_zones = AnsibleCloudStackZones(module)
result = acs_zones.get_zones()
module.exit_json(**result)
if __name__ == '__main__':
main()

@ -0,0 +1,2 @@
---
cloudstack_venv: "{{ playbook_dir }}/configs/.venvs/cloudstack"

@ -0,0 +1,71 @@
---
- block:
- name: Build python virtual environment
import_tasks: venv.yml
- name: Include prompts
import_tasks: prompts.yml
- block:
- set_fact:
algo_region: >-
{% if region is defined %}{{ region }}
{%- elif _algo_region.user_input is defined and _algo_region.user_input | length > 0 %}{{ cs_zones[_algo_region.user_input | int -1 ]['name'] }}
{%- else %}{{ cs_zones[default_zone | int - 1]['name'] }}{% endif %}
- name: Security group created
cs_securitygroup:
name: "{{ algo_server_name }}-security_group"
description: AlgoVPN security group
register: cs_security_group
- name: Security rules created
cs_securitygroup_rule:
security_group: "{{ cs_security_group.name }}"
protocol: "{{ item.proto }}"
start_port: "{{ item.start_port }}"
end_port: "{{ item.end_port }}"
cidr: "{{ item.range }}"
with_items:
- { proto: tcp, start_port: 22, end_port: 22, range: 0.0.0.0/0 }
- { proto: udp, start_port: 4500, end_port: 4500, range: 0.0.0.0/0 }
- { proto: udp, start_port: 500, end_port: 500, range: 0.0.0.0/0 }
- { proto: udp, start_port: "{{ wireguard_port }}", end_port: "{{ wireguard_port }}", range: 0.0.0.0/0 }
- name: Keypair created
cs_sshkeypair:
name: "{{ SSH_keys.comment|regex_replace('@', '_') }}"
public_key: "{{ lookup('file', '{{ SSH_keys.public }}') }}"
register: cs_keypair
- name: Set facts
set_fact:
image_id: "{{ cloud_providers.cloudstack.image }}"
size: "{{ cloud_providers.cloudstack.size }}"
disk: "{{ cloud_providers.cloudstack.disk }}"
keypair_name: "{{ cs_keypair.name }}"
- name: Server created
cs_instance:
name: "{{ algo_server_name }}"
root_disk_size: "{{ disk }}"
template: "{{ image_id }}"
ssh_key: "{{ keypair_name }}"
security_groups: "{{ cs_security_group.name }}"
zone: "{{ algo_region }}"
service_offering: "{{ size }}"
register: cs_server
- set_fact:
cloud_instance_ip: "{{ cs_server.default_ip }}"
ansible_ssh_user: ubuntu
environment:
PYTHONPATH: "{{ cloudstack_venv }}/lib/python2.7/site-packages/"
CLOUDSTACK_CONFIG: "{{ algo_cs_config }}"
CLOUDSTACK_REGION: "{{ algo_cs_region }}"
rescue:
- debug: var=fail_hint
tags: always
- fail:
tags: always

@ -0,0 +1,55 @@
---
- block:
- pause:
prompt: |
Enter path for cloudstack.ini file (https://trailofbits.github.io/algo/cloud-cloudstack.html)
[~/.cloudstack.ini]
register: _cs_config
when:
- cs_config is undefined
- lookup('env', 'CLOUDSTACK_CONFIG') | length <= 0
- pause:
prompt: |
Specify region to use in cloudstack.ini file
[exoscale]
register: _cs_region
when:
- cs_region is undefined
- lookup('env', 'CLOUDSTACK_REGION') | length <= 0
- set_fact:
algo_cs_config: "{{ cs_config | default(_cs_config.user_input|default(None)) | default(lookup('env', 'CLOUDSTACK_CONFIG'), true) | default('~/.cloudstack.ini', true) }}"
algo_cs_region: "{{ cs_region | default(_cs_region.user_input|default(None)) | default(lookup('env', 'CLOUDSTACK_REGION'), true) | default('exoscale', true) }}"
- name: Get zones on cloud
cloudstack_zones:
register: _cs_zones
environment:
CLOUDSTACK_CONFIG: "{{ algo_cs_config }}"
CLOUDSTACK_REGION: "{{ algo_cs_region }}"
- name: Extract zones from output
set_fact:
cs_zones: "{{ _cs_zones['zone'] | sort(attribute='name') }}"
- name: Set the default zone
set_fact:
default_zone: >-
{% for z in cs_zones %}
{%- if z['name'] == "ch-gva-2" %}{{ loop.index }}{% endif %}
{%- endfor %}
- pause:
prompt: |
What zone should the server be located in?
{% for z in cs_zones %}
{{ loop.index }}. {{ z['name'] }}
{% endfor %}
Enter the number of your desired zone
[{{ default_zone }}]
register: _algo_region
when: region is undefined
environment:
PYTHONPATH: "{{ cloudstack_venv }}/lib/python2.7/site-packages/"

@ -0,0 +1,15 @@
---
- name: Clean up the environment
file:
dest: "{{ cloudstack_venv }}"
state: absent
when: clean_environment
- name: Install requirements
pip:
name:
- cs
- sshpubkeys
state: latest
virtualenv: "{{ cloudstack_venv }}"
virtualenv_python: python2.7
Loading…
Cancel
Save