ansible/cloud_providers/ravello/Ansible_Ravello_module.adoc
New file @@ -0,0 +1,224 @@ :scrollbar: :data-uri: :toc2: == Requirements & Instructions on Using the Ansible Ravello Module :numbered: == Requirements . Install Ansible `http://docs.ansible.com/ansible/intro_installation.html` . Install the Ravello SDK via **pip** + NOTE: For manual setup instructions see `https://github.com/ravello/python-sdk` and for additional documentation see `http://ravello-sdk.readthedocs.io/en/ravello-sdk-1.4/` + ---- pip install ravello-sdk ---- === Test Ravello SDK . Clone the ravello-sdk locally to use the example scripts + ---- cd <desired_dir> git clone https://github.com/ravello/python-sdk.git ---- + . Change to the **examples** directory + ---- cd python-sdk/examples ---- + . Set up credentials file for Ravello using your username and password + ---- ./set-creds Enter username: your_login@redhat.com Enter a Password: ---- + . Verify saved credentials + NOTE: This will display your login into on the terminal however the password is hashed. + ---- cat ~/.ravello_login <username> <password> ---- + . Test the script to get all blueprints you have access to + ---- ./bp-get-all <all blueprints you have access to> ---- === Test Capture of Ravello Environment Metadata to JSON . Test the script to get all applications you have access to + ---- ./app-get-all <all apps you have access to> ---- + . Test the script to get all metadata for a specific application and write it to a JSON file + NOTE: The option **-a** is for a full JSON dump of extended application metadata + ---- ./app-get-data -a <one app name from output in previous step > myapp.json ---- == Using the Ravello Ansible Module === Clone the Ansible Ravello Repository . Change to a directory of your chosing + ---- git clone https://github.com/RedHatDemos/ansible-ravello.git ---- === Create YAML Template . Within the cloned repository directory, create template from existing app: + ---- ./app-get-data -a <your_application> 2>/dev/null|python -c 'import sys, yaml, json; yaml.safe_dump(json.load(sys.stdin), sys.stdout, default_flow_style=False)' > out.yaml ---- . Remove everything that is not in the *design:* section. This is at the beginning and end of the file. . Under *design:* remove all subsections except *vms:* . Remove any line with *applicationId:*, *creationTime:*, *loadingStatus:*, *loadingPercentage:*, *fqdn:*, *allocatedIp:*, *externalAccessState:*, *ipConfigLuid:*, and *useLuidForIpConfig:*. === Deploy an Environment . This playbook will create a blueprint based on a set of virtual machines described in *app_template.yml*. It will then create an application from this blueprint and wait for it to start. . To use the default values just run the *deploy_environment.yml. + ---- ansible-playbook deploy_environment.yml ---- + NOTE: It is likely there may be a conflict if the default names have already been used. Instead, specify a *unique_name* and optionally a *version* which will be used to make sure your blueprints and applications have unique names + . Specify *unique_name* and *version* if desired: + ---- ansible-playbook deploy_environment.yml -e "unique_name=vvaldez-demo version=1.6" ---- . It is also possible to load your own variables from a yaml file: + ---- ansible-playbook deploy_environment.yml -e @vars.yml ---- . Contents of *vars.yml*: + ---- unique_name=vvaldez-demo version=1.6 ---- . To skip the blueprint creation phase, just provide an existing *blueprint_id*: + ---- ansible-playbook deploy_environment.yml -e "blueprint_id=12345678" ---- . Example *deploy_environment.yml* playbook template provided in this repo: + ---- --- - hosts: localhost tasks: # Create app, based on blueprint, start it and wait for started - local_action: module: ravello_app # Make sure you update the version number as the name must be unique name: 'example_app-v1.0' description: 'Example blueprint created by ansible' app_template: '/home/user/ansible-ravello/app_template.yml' state: design ---- + [NOTE] The *name* field must be unique. All blueprints must have a unique name. Typically this is achieved by changing the version number in the name. === Create Blueprint From Template ---- ansible-playbook design.yml -vvv ---- ---- Using /etc/ansible/ansible.cfg as config file [WARNING]: provided hosts list is empty, only localhost is available PLAYBOOK: design.yml *********************************************************** 1 plays in design.yml PLAY [localhost] *************************************************************** TASK [setup] ******************************************************************* Using module file /usr/lib/python2.6/site-packages/ansible/modules/core/system/setup.py <127.0.0.1> ESTABLISH LOCAL CONNECTION FOR USER: prutledg <127.0.0.1> EXEC /bin/sh -c '( umask 77 && mkdir -p "` echo $HOME/.ansible/tmp/ansible-tmp-1481666189.73-18007598170313 `" && echo ansible-tmp-1481666189.73-18007598170313="` echo $HOME/.ansible/tmp/ansible-tmp-1481666189.73-18007598170313 `" ) && sleep 0' <127.0.0.1> PUT /tmp/tmp7TPuB1 TO /home/prutledg/.ansible/tmp/ansible-tmp-1481666189.73-18007598170313/setup.py <127.0.0.1> EXEC /bin/sh -c 'chmod u+x /home/prutledg/.ansible/tmp/ansible-tmp-1481666189.73-18007598170313/ /home/prutledg/.ansible/tmp/ansible-tmp-1481666189.73-18007598170313/setup.py && sleep 0' <127.0.0.1> EXEC /bin/sh -c '/usr/bin/python2.6 /home/prutledg/.ansible/tmp/ansible-tmp-1481666189.73-18007598170313/setup.py; rm -rf "/home/prutledg/.ansible/tmp/ansible-tmp-1481666189.73-18007598170313/" > /dev/null 2>&1 && sleep 0' ok: [localhost] TASK [ravello_app] ************************************************************* task path: /home/prutledg/ansible-ravello/design.yml:5 Using module file /home/prutledg/ansible-ravello/library/ravello_app.py <localhost> ESTABLISH LOCAL CONNECTION FOR USER: prutledg <localhost> EXEC /bin/sh -c '( umask 77 && mkdir -p "` echo $HOME/.ansible/tmp/ansible-tmp-1481666190.57-63460892520707 `" && echo ansible-tmp-1481666190.57-63460892520707="` echo $HOME/.ansible/tmp/ansible-tmp-1481666190.57-63460892520707 `" ) && sleep 0' <localhost> PUT /tmp/tmp7TPuB1 TO /home/prutledg/.ansible/tmp/ansible-tmp-1481666190.57-63460892520707/ravello_app.py <localhost> EXEC /bin/sh -c 'chmod u+x /home/prutledg/.ansible/tmp/ansible-tmp-1481666190.57-63460892520707/ /home/prutledg/.ansible/tmp/ansible-tmp-1481666190.57-63460892520707/ravello_app.py && sleep 0' <localhost> EXEC /bin/sh -c '/usr/bin/python2.6 /home/prutledg/.ansible/tmp/ansible-tmp-1481666190.57-63460892520707/ravello_app.py; rm -rf "/home/prutledg/.ansible/tmp/ansible-tmp-1481666190.57-63460892520707/" > /dev/null 2>&1 && sleep 0' changed: [localhost -> localhost] => { "blueprint_id": "76743737", "changed": true, "invocation": { "module_args": { "app_template": "/home/prutledg/ansible-ravello/app_template.yml", "application_ttl": -1, "blueprint_description": null, "blueprint_id": null, "blueprint_name": null, "cloud": null, "description": "Example blueprint created by ansible", "name": "example_app-v1.0", "password": null, "publish_optimization": "cost", "region": null, "service_name": "ssh", "state": "design", "url": null, "username": null, "wait": true, "wait_timeout": 1200 }, "module_name": "ravello_app" }, "name": "example_app-v1.0" } ---- === Check for Blueprint . Log into Ravello and check if the blueprint exists. . The next steps would be to deploy the blueprint with this ansible module as a new application, inventory the app, and apply final playbook(s) to the resulting VMs then create final blueprint from that. ansible/cloud_providers/ravello/README.md
New file @@ -0,0 +1,12 @@ # Synopsis The Ansible Ravello module is intended to provide an easy way to interact with Ravello using the Ravello SDK to interact with the Ravello API. The goal is to use the automation capabilities of Ansible to easily deploy new applications, customize the applications and save the fully configured application back as a blueprint in Ravello allow for quick, scalable, pre-configured deployments. # Pre-reqs, Installation, Examples Refer to: https://github.com/RedHatDemos/ansible-ravello/blob/master/Ansible_Ravello_module.adoc for installation dependencies and module usage. # Contributors The main contributors for this project are: </br> *Pat Rutledge* prutledge@redhat.com </br> *Vinny Valdez* vvaldez@redhat.com </br> *Nenad Peric* nperic@redhat.com </br> *Brett Thurber* bthurber@redhat.com ansible/cloud_providers/ravello/app_template.yml
New file @@ -0,0 +1,190 @@ vms: - name: "Bastion Host" tag: "bastion" description: "Bastion Host\nnohbac: true\n" numCpus: 1 memorySize: unit: "GB" value: 1 hostnames: - "bastion-REPL.rhpds.opentlc.com" - "bastion.example.com" hardDrives: - index: 0 imageName: "rhel-guest-image-7.3-35.x86_64" boot: true controller: "virtio" name: "root disk" size: unit: "GB" value: 40 type: "DISK" networkConnections: - name: "eth0" device: index: 0 deviceType: "virtio" useAutomaticMac: false mac: "2c:c2:60:14:42:52" ipConfig: autoIpConfig: reservedIp: "192.168.1.10" hasPublicIp: true - name: "eth1" device: index: 1 deviceType: "virtio" useAutomaticMac: true ipConfig: autoIpConfig: reservedIp: "192.168.2.10" stopTimeOut: "300" suppliedServices: - external: true ip: "192.168.1.10" name: "ssh" portRange: "22" protocol: "SSH" supportsCloudInit: true keypairName: "opentlc-admin-backdoor" - name: "App Server" tag: "app_server" description: "App Server" numCpus: 2 memorySize: unit: "GB" value: 4 hostnames: - "app-REPL.rhpds.opentlc.com" - "app.example.com" hardDrives: - index: 0 imageName: "rhel-guest-image-7.3-35.x86_64" boot: true controller: "virtio" name: "root disk" size: unit: "GB" value: 40 type: "DISK" networkConnections: - name: "eth0" device: index: 0 deviceType: "virtio" useAutomaticMac: true ipConfig: autoIpConfig: reservedIp: "192.168.1.20" - name: "eth1" device: index: 1 deviceType: "virtio" useAutomaticMac: true ipConfig: autoIpConfig: reservedIp: "192.168.2.20" stopTimeOut: "300" - name: "Web Server" tag: "web_server" description: "Web Server" numCpus: 2 memorySize: unit: "GB" value: 2 hostnames: - "www-REPL.rhpds.opentlc.com" - "www.example.com" hardDrives: - index: 0 imageName: "rhel-guest-image-7.3-35.x86_64" boot: true controller: "virtio" name: "root disk" size: unit: "GB" value: 40 type: "DISK" - index: 1 controller: "virtio" name: "blank web root disk" size: unit: "GB" value: 10 type: "DISK" networkConnections: - name: "eth0" device: index: 0 deviceType: "virtio" useAutomaticMac: true ipConfig: autoIpConfig: reservedIp: "192.168.1.30" hasPublicIp: true stopTimeOut: "300" suppliedServices: - ip: "192.168.1.30" name: "http" portRange: "80" protocol: "HTTP" - ip: "192.168.1.30" name: "https" portRange: "443" protocol: "HTTPS" - ip: "192.168.1.30" name: "example TCP" portRange: "8080" protocol: "TCP" - ip: "192.168.1.30" name: "example UDP" portRange: "901" protocol: "UDP" supportsCloudInit: true keypairName: "opentlc-admin-backdoor" - name: "DB Server" tag: "db_server" description: "DB Server" numCpus: 4 memorySize: unit: "GB" value: 4 hostnames: - "db-REPL.rhpds.opentlc.com" - "db.example.com" hardDrives: - index: 0 imageName: "rhel-guest-image-7.3-35.x86_64" boot: true controller: "virtio" name: "root disk" size: unit: "GB" value: 40 type: "DISK" - index: 1 controller: "virtio" name: "blank db disk" size: unit: "GB" value: 20 type: "DISK" networkConnections: - name: "eth0" device: index: 0 deviceType: "virtio" useAutomaticMac: true ipConfig: autoIpConfig: reservedIp: "192.168.1.40" - name: "eth1" device: index: 1 deviceType: "virtio" useAutomaticMac: true ipConfig: autoIpConfig: reservedIp: "192.168.2.40" stopTimeOut: "300" ansible/cloud_providers/ravello/app_template_short.yml
New file @@ -0,0 +1,49 @@ vms: - name: "Bastion Host" tag: "bastion" description: "Bastion Host\nnohbac: true\n" numCpus: 1 memorySize: unit: "GB" value: 1 hostnames: - "bastion-REPL.rhpds.opentlc.com" - "bastion.example.com" hardDrives: - index: 0 imageName: "rhel-guest-image-7.3-35.x86_64" boot: true controller: "virtio" name: "root disk" size: unit: "GB" value: 40 type: "DISK" networkConnections: - name: "eth0" device: index: 0 deviceType: "virtio" useAutomaticMac: false mac: "2c:c2:60:14:42:52" ipConfig: autoIpConfig: reservedIp: "192.168.1.10" hasPublicIp: true - name: "eth1" device: index: 1 deviceType: "virtio" useAutomaticMac: true ipConfig: autoIpConfig: reservedIp: "192.168.2.10" stopTimeOut: "300" suppliedServices: - external: true ip: "192.168.1.10" name: "ssh" portRange: "22" protocol: "SSH" supportsCloudInit: true keypairName: "opentlc-admin-backdoor" ansible/cloud_providers/ravello/deploy_environment.yml
New file @@ -0,0 +1,10 @@ --- - hosts: localhost roles: - role: warm_up - role: blueprint_design when: not blueprint_id is defined - role: application_create_from_blueprint blueprint_id: "{{ design_results.blueprint_id }}" - role: blueprint_create_from_application application_name: "{{ app_results.app_name }}" ansible/cloud_providers/ravello/group_vars/all
New file @@ -0,0 +1,9 @@ --- unique_name: 'example' version: '1.0' wait_timeout: '1800' blueprint_name: "{{ 'ansible-' + unique_name + '-v' + version }}" blueprint_description: "{{ unique_name + ' blueprint created by ansible' }}" application_name: "{{ 'ansible-' + unique_name + '-app-v' + version }}" application_description: "{{ unique_name + ' application created by ansible' }}" app_template: "app_template.yml" ansible/cloud_providers/ravello/inventory/ansible_tower_ravello_inventory.py
New file @@ -0,0 +1,218 @@ #!/usr/bin/python ''' Ravello external inventory script ================================================== Generates inventory that Ansible can understand by making an API request to Ravello. Modeled after https://raw.githubusercontent.com/jameslabocki/ansible_api/master/python/ansible_tower_cloudforms_inventory.py Required: Ravello Python SDK https://github.com/ravello/python-sdk Useful: https://www.ravellosystems.com/ravello-api-doc/ Notes: In my testing, with >200 applications and ~1,000 virtual machines this took 30 seconds to execute. If the get_applications call in the Ravello Python SDK supported dumping design information this could be dramatically reduced. jlabocki <at> redhat.com or @jameslabocki on twitter ''' import os import re import argparse import ConfigParser import requests import json from argparse import ArgumentParser import base64 import getpass import logging import logging.handlers from ravello_sdk import * def get_credentials(): with open(os.path.expanduser("~/.ravello_login"),"r") as pf: username = pf.readline().strip() encrypted_password = pf.readline().strip() password = base64.b64decode(encrypted_password).decode() return username,password def get_user_credentials(username): password = None if username: password = getpass.getpass('Enter a Password: ') else: #read user credentials from .ravello_login file in user HOMEDIR username,password = get_credentials() if not username or not password: log.error('User credentials are not set') print('Error: User credentials are not set') return None,None return username,password def connect(username, password): client = RavelloClient() try: client.login(username, password) except Exception as e: print('Error: Invalid user credentials, username {0}'.format(username)) return None return client def get_app_id(app_name,client): app_id=0 for app in client.get_applications(): if app['name'].lower() == app_name.lower(): app_id = app['id'] break return app_id class RavelloInventory(object): def _empty_inventory(self): return {"_meta" : {"hostvars" : {}}} def __init__(self): ''' Main execution path ''' # Inventory grouped by instance IDs, tags, security groups, regions, # and availability zones self.inventory = self._empty_inventory() # Index of hostname (address) to instance ID self.index = {} # Read CLI arguments self.read_settings() self.parse_cli_args() # If --apps is set then run get_apps_all #if self.args.apps is True: # self.get_apps_all() # If --list is set then run get_app with ID of application if self.args.list is not None: self.get_app() def parse_cli_args(self): ''' Command line argument processing ''' parser = argparse.ArgumentParser(description='Produce an Ansible Inventory file based on Ravello') parser.add_argument('--apps', action='store_false', help='List all app names (default: False)') parser.add_argument('--list', action='store', default=False, help='Get the group(s) and hostname(s) from a specific application by specifying the app name') self.args = parser.parse_args() def read_settings(self): ''' Reads the settings from the ravello.ini file ''' config = ConfigParser.SafeConfigParser() config_paths = [ os.path.join(os.path.dirname(os.path.realpath(__file__)), 'ravello.ini'), "/etc/ansible/ravello.ini", ] env_value = os.environ.get('RAVELLO_INI_PATH') if env_value is not None: config_paths.append(os.path.expanduser(os.path.expandvars(env_value))) config.read(config_paths) # Get Auth from INI INI=True if config.has_option('ravello', 'username'): self.ravello_username = config.get('ravello', 'username') else: self.ravello_username = "none" INI=False if config.has_option('ravello', 'password'): self.ravello_password = config.get('ravello', 'password') else: self.ravello_password = "none" INI=False if INI is False: self.ravello_username, self.ravello_password = get_user_credentials(None) if not self.ravello_username or not self.ravello_password: print("ERROR: Could not get Ravello credentials from INI file or .ravello_login (SDK Auth)") exit(1) def get_apps_all(self): #Connect to Ravello client = connect(self.ravello_username, self.ravello_password) if not client: exit (1) apps = client.get_applications() names = [] for app in apps: #Only get the published apps if app['published']: myname = (json.dumps(app['name'])) names.append(myname) for name in names: print name def get_app(self): #Connect to Ravello myappname = self.args.list client = connect(self.ravello_username, self.ravello_password) if not client: exit (1) apps = client.get_applications() myappid = "" for app in apps: #Only get the published apps if app['published']: if str(app['name']) == myappname: myappid = app['id'] #First, define empty lists for the the tags, groups, subgroups for tags/vms, and the formatted list for tower. groups = {} groups['_meta'] = {} groups['_meta']['hostvars'] = {} app = client.get_application(myappid, aspect="deployment") if app['deployment']: appname = app['name'] vmsFlag = True if "vms" in app["deployment"] else False if vmsFlag == True: vms = app['deployment']['vms'] for vm in vms: #if 'externalFqdn' in vm: # hostname = vm['externalFqdn'] #else: hostnames = vm['hostnames'] hostname = hostnames[0] desc = vm['description'] for line in desc.splitlines(): if re.match("^tag:", line): t = line.split(':') tag = t[1] if tag in groups.keys(): groups[tag]['hosts'].append(hostname) else: groups[tag] = {} groups[tag]['hosts'] = {} groups[tag]['hosts'] = [hostname] if 'externalFqdn' in vm: groups['_meta']['hostvars'][hostname] = { 'externalFqdn': vm['externalFqdn'] } if tag == 'bastion' and 'externalFqdn' in vm: groups['_meta']['hostvars'][hostname].update({ 'bastion': True }) print json.dumps(groups, indent=5) #Run the script RavelloInventory() ansible/cloud_providers/ravello/inventory/ravello.ini
New file @@ -0,0 +1,11 @@ # Ansible Ravello external inventory script settings # Uncomment and add your credentials # Alternative, use SDK to configure ~/.ravello_login [ravello] # Username for ravello #username = yourusername # Password for ravello #password = yourpassword ansible/cloud_providers/ravello/library/ravello_app.py
New file @@ -0,0 +1,672 @@ #!/usr/bin/python # (c) 2015, ravellosystems # # author zoza # # # Ansible is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # Ansible is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Ansible. If not, see <http://www.gnu.org/licenses/>. ###################################################################### import random, string try: from ravello_sdk import * HAS_RAVELLO_SDK = True except ImportError: HAS_RAVELLO_SDK = False except ImportError: print "failed=True msg='ravello sdk required for this module'" sys.exit(1) from ravello_cli import get_diskimage DOCUMENTATION = ''' --- module: ravello_app short_description: Create/delete/start/stop an application in ravellosystems description: - Create/delete/start/stop an application in ravellosystems and wait for it (optionally) to be 'running' - list state will return a fqdn list of exist application hosts with their external services - blueprint state wil create a blueprint from an existing app (must provide blueprint_name) options: state: description: - Indicate desired state of the target. default: present choices: ['design', 'present', 'started', 'absent', 'stopped','list','blueprint'] username: description: - ravello username password: description: - ravello password service_name: description: - Supplied Service name for list state default: ssh name: description: - application name description: description: - application description blueprint_id: description: - create app, based on this blueprint #publish options cloud: description: - cloud to publish region: description: - region to publish publish_optimization: default: cost choices: ['cost', 'performance'] application_ttl: description: - application autostop in mins default: -1 # never wait description: - Wait for the app to be in state 'running' before returning. default: True choices: [ True, False ] wait_timeout: description: - How long before wait gives up, in seconds. default: 600 blueprint_name: description: - Specify a name for a new blueprint based on existing app blueprint_description: description: - Description of new blueprint app_template: description: - Path to a YML file that defines an application infrastructure then creates a blueprint for further processing with follow-on playbooks. Must use state=design ''' EXAMPLES = ''' # Create app, based on blueprint, start it and wait for started - local_action: module: ravello_app username: user@ravello.com password: password name: 'my-application-name' description: 'app desc' blueprint_id: '2452' wait: True wait_timeout: 600 state: present # Create app, based on blueprint - local_action: module: ravello_app username: user@ravello.com password: password name: 'my-application-name' description: 'app desc' publish_optimization: performance cloud:AMAZON region: Oregon state: present # List application example - local_action: module: ravello_app name: 'my-application-name' service_name: 'ssh' state: list # Delete application example - local_action: module: ravello_app name: 'my-application-name' state: absent # Create blueprint from existing app - local_action: module: ravello_app name: 'my-application-name' blueprint_name: 'my-application-bp' blueprint_description: 'Blueprint of app xyz' state: blueprint # Create blueprint based on app_template.yml - local_action: module: ravello_app name: 'my-new-baseline' description: 'My new baseline' app_template: 'app_template.yml' state: design register: design_results ''' import os import base64 import getpass import logging import logging.handlers def get_credentials(): with open(os.path.expanduser("~/.ravello_login"),"r") as pf: username = pf.readline().strip() encrypted_password = pf.readline().strip() password = base64.b64decode(encrypted_password).decode() return username,password def get_user_credentials(username): password = None if username: password = getpass.getpass('Enter a Password: ') else: #read user credentials from .ravello_login file in user HOMEDIR username,password = get_credentials() if not username or not password: log.error('User credentials are not set') print('Error: User credentials are not set') return None,None return username,password def initlog(log_file): logger = logging.getLogger() logger.setLevel(logging.INFO) logpath=os.path.join(os.getcwd(),log_file) handler = logging.handlers.RotatingFileHandler(logpath, maxBytes=1048576, backupCount=10) fmt = '%(asctime)s: %(filename)-20s %(levelname)-8s %(message)s' handler.setFormatter(logging.Formatter(fmt)) logger.addHandler(handler) def connect(username, password): client = RavelloClient() try: client.login(username, password) except Exception as e: sys.stderr.write('Error: {!s}\n'.format(e)) log.error('Invalid user credentials, username {0}'.format(username)) print('Error: Invalid user credentials, username {0}'.format(username)) return None return client def get_app_id(app_name,client): app_id=0 for app in client.get_applications(): if app['app_name'].lower() == app_name.lower(): app_id = app['id'] break if app_id == 0: module.fail_json(msg = 'ERROR: Cloud not find app: %s' % app_name) return app_id def get_blueprint_id(blueprint_name,client): blueprint_id=0 for blueprint in client.get_blueprints(): if blueprint['name'].lower() == blueprint_name.lower(): blueprint_id = blueprint['id'] break if blueprint_id == 0: module.fail_json(msg = 'ERROR: Cloud not find blueprint: %s' % blueprint_name) return blueprint_id def get_image_id(image_name,client): image_id=0 for image in client.get_images(): if image['name'].lower() == image_name.lower(): image_id = image['id'] break if image_id == 0: module.fail_json(msg = 'ERROR: Cloud not find VM image named: %s' % image_name) return image_id def get_image(image_id,client): try: image = client.get_image(image_id) except Exception as e: module.fail_json(msg = 'ERROR: Cloud not find VM image id: %s' % image_id) return image def main(): ch = logging.StreamHandler(log_capture_string) ch.setLevel(logging.DEBUG) ### Optionally add a formatter formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s') ch.setFormatter(formatter) ### Add the console handler to the logger logger.addHandler(ch) argument_spec=dict( # for nested babu only url=dict(required=False, type='str'), state=dict(default='present', choices=['design', 'present', 'started', 'absent', 'stopped', 'list', 'test', 'blueprint','blueprint_delete','blueprint_location']), username=dict(required=False, type='str'), password=dict(required=False, type='str'), name=dict(required=False, type='str'), app_name=dict(required=False, type='str'), description=dict(required=False, type='str'), blueprint_id=dict(required=False, type='str'), app_template=dict(required=False, default=None, type='path'), cloud=dict(required=False, type='str'), region=dict(required=False, type='str'), publish_optimization=dict(default='cost', choices=['cost', 'performance']), application_ttl=dict(default='-1', type='int'), service_name=dict(default='ssh', type='str'), blueprint_description=dict(required=False, type='str'), blueprint_name=dict(required=False, type='str'), wait=dict(type='bool', default=True ,choices=BOOLEANS), wait_timeout=dict(default=1200, type='int') ) module = AnsibleModule( argument_spec=argument_spec, mutually_exclusive=[['blueprint', 'app_template']], # We really really should support this... # supports_check_mode = True ) if not HAS_RAVELLO_SDK: module.fail_json(msg='ravello_sdk required for this module') # Get User credentials from Ansible (not too secure) or ENV variables (a little more secure) username = module.params.get('username', os.environ.get('RAVELLO_USERNAME', None)) password = module.params.get('password', os.environ.get('RAVELLO_PASSWORD', None)) if username and password: try: client = RavelloClient(username, password, module.params.get('url')) except Exception, e: log_contents = log_capture_string.getvalue() log_capture_string.close() module.fail_json(msg = 'ERROR: Failed to authenticate to Ravello using ansiblie provided credentials %s' % e,stdout='%s' % log_contents) else: #Get user credentials from SDK auth cache file (better) try: username, password = get_user_credentials(None) except Exception, e: log_contents = log_capture_string.getvalue() log_capture_string.close() module.fail_json(msg = 'ERROR: Failed to retrieve credentials from Ravello SDK credentials cache %s' % e,stdout='%s' % log_contents) if not username or not password: module.fail_json(msg = 'ERROR: Unable to get any Ravello credentials!') try: client = connect(username, password) except Exception, e: log_contents = log_capture_string.getvalue() log_capture_string.close() module.fail_json(msg = 'ERROR: Failed to authenticate to Ravello using Ravello SDK credentials cache %s' % e,stdout='%s' % log_contents) if module.params.get('state') == 'design': create_app(client, module) elif module.params.get('state') == 'present': create_app_and_publish(client, module) elif module.params.get('state') == 'absent': action_on_app(module, client, client.delete_application, lambda: None, 'Deleted') elif module.params.get('state') == 'started': action_on_app(module, client, client.start_application, functools.partial(_wait_for_state,client,'STARTED',module), 'Started') elif module.params.get('state') == 'stopped': action_on_app(module, client, client.stop_application, functools.partial(_wait_for_state,client,'STOPPED',module), 'Stopped') elif module.params.get('state') == 'list': list_app(client, module) elif module.params.get('state') == 'blueprint': create_blueprint(module, client, client.create_blueprint) elif module.params.get('state') == 'blueprint_delete': action_on_blueprint(module, client, client.delete_blueprint) elif module.params.get('state') == 'blueprint_location': action_on_blueprint(module, client, client.get_blueprint_publish_locations) elif module.params.get('state') == 'test': module.exit_json(msg = 'Authentication to Ravello successful') def _wait_for_state(client, state, module): if module.params.get('wait') == False: return wait_timeout = module.params.get('wait_timeout') app_id = 0 wait_till = time.time() + wait_timeout while wait_till > time.time(): if app_id > 0: app = client.get_application(app_id) else: app = client.get_application_by_name(module.params.get('app_name')) app_id = app['id'] states = list(set((vm['state'] for vm in app.get('deployment', {}).get('vms', [])))) if "ERROR" in states: log_contents = log_capture_string.getvalue() log_capture_string.close() module.fail_json(msg = 'Vm got ERROR state',stdout='%s' % log_contents) if len(states) == 1 and states[0] == state: return time.sleep(10) log_contents = log_capture_string.getvalue() log_capture_string.close() module.fail_json(msg = 'Timed out waiting for async operation to complete.', stdout='%s' % log_contents) def is_wait_for_external_service(supplied_service,module): return supplied_service['name'].lower() == module.params.get('service_name').lower() and supplied_service['external'] == True def get_list_app_vm_result(app, vm, module): for supplied_service in vm['suppliedServices']: if is_wait_for_external_service(supplied_service, module): for network_connection in vm['networkConnections']: if network_connection['ipConfig']['id'] == supplied_service['ipConfigLuid']: dest = network_connection['ipConfig'].get('fqdn') port = int(supplied_service['externalPort'].split(",")[0].split("-")[0]) return (dest,port) def list_app(client, module): try: app_name = module.params.get("app_name") app = client.get_application_by_name(app_name) results = [] for vm in app['deployment']['vms']: if vm['state'] != "STARTED": continue (dest,port) = get_list_app_vm_result(app, vm, module) results.append({'host': dest, 'port': port}) log_contents = log_capture_string.getvalue() log_capture_string.close() module.exit_json(changed=True, app_name='%s' % app_name, results='%s' % results,stdout='%s' % log_contents) except Exception, e: log_contents = log_capture_string.getvalue() log_capture_string.close() module.fail_json(msg = '%s' % e,stdout='%s' % log_contents) def action_on_app(module, client, runner_func, waiter_func, action): try: app_name = module.params.get("app_name") app = client.get_application_by_name(app_name) runner_func(app['id']) waiter_func() log_contents = log_capture_string.getvalue() log_capture_string.close() module.exit_json(changed=True, app_name='%s application: %s' %(action, app_name),stdout='%s' % log_contents) except Exception, e: log_contents = log_capture_string.getvalue() log_capture_string.close() module.fail_json(msg = '%s' % e,stdout='%s' % log_contents) def create_blueprint(module, client, runner_func): app_name = module.params.get("app_name") app = client.get_application_by_name(app_name) blueprint_name = module.params.get("blueprint_name") blueprint_description = module.params.get("blueprint_description") blueprint_dict = {"applicationId":app['id'], "blueprintName":blueprint_name, "offline": True, "description":blueprint_description } try: blueprint_id=((runner_func(blueprint_dict))['_href'].split('/'))[2] log_contents = log_capture_string.getvalue() log_capture_string.close() module.exit_json(changed=True, app_name='%s' % app_name, blueprint_name='%s' % blueprint_name, blueprint_id='%s' % blueprint_id) except Exception, e: log_contents = log_capture_string.getvalue() log_capture_string.close() module.fail_json(msg = '%s' % e,stdout='%s' % log_contents) def action_on_blueprint(module, client, runner_func): if module.params.get("blueprint_id"): blueprint_id = module.params.get("blueprint_id") elif module.params.get("blueprint_name"): blueprint_name = module.params.get("blueprint_name") blueprint_id = get_blueprint_id(blueprint_name, client) try: output = runner_func(blueprint_id) log_contents = log_capture_string.getvalue() log_capture_string.close() module.exit_json(changed=True, stdout='%s' % log_contents, blueprint_id='%s' % blueprint_id, output='%s' % output) except Exception, e: log_contents = log_capture_string.getvalue() log_capture_string.close() module.fail_json(msg = '%s' % e,stdout='%s' % log_contents) def create_app(client, module): app_name = module.params.get("app_name") cap = client.get_applications({'name': app_name}) if cap: module.fail_json(msg='ERROR: Application %s already exists!' % app_name, changed=False) blueprint_name = app_name + "-bp" bp = client.get_blueprints({'name': blueprint_name}) if bp: module.fail_json(msg='ERROR: Blueprint %s already exists!' % blueprint_name, changed=False) app_description = module.params.get("description") if not module.params.get("app_template"): module.fail_json(msg='Must supply an app_template for design state.', changed=False) app_template = module.params.get("app_template") with open(app_template, 'r') as data: try: read_app = yaml.load(data) except yaml.YAMLError as exc: print(exc) rand_str = lambda n: ''.join([random.choice(string.lowercase) for i in xrange(n)]) new_app = {} new_app['name'] = "tmp-app-build-" + rand_str(10) new_app['description'] = app_description new_app['design'] = {} new_app['design']['vms'] = [] for vm in read_app['vms']: pubip = False if not 'description' in vm: vm['description'] = "" if 'tag' in vm: vm['description'] = vm['description'] + "\ntag:" + vm['tag'] + "\n" if not 'numCpus' in vm: module.fail_json(msg = 'ERROR numCpus not specified for VM!') new_vm = {'name': vm['name'], 'description': vm['description'], 'baseVmId': 0, 'os': 'linux_manuel', 'numCpus': vm['numCpus'] } if 'hostnames' in vm: new_vm['hostnames'] = vm['hostnames'] if not 'memorySize' in vm: module.fail_json(msg = 'ERROR memorySize subsection not specified for VM!') else: new_vm['memorySize'] = { 'unit': vm['memorySize']['unit'], 'value': vm['memorySize']['value'] } if 'keypairName' in vm: new_vm['keypairName'] = vm['keypairName'] if 'supportsCloudInit' in vm: new_vm['supportsCloudInit'] = vm['supportsCloudInit'] if 'stopTimeOut' in vm: new_vm['stopTimeOut'] = vm['stopTimeOut'] else: new_vm['stopTimeOut'] = 300 if 'allowNested' in vm: new_vm['allowNested'] = vm['allowNested'] if 'bootOrder' in vm: new_vm['bootOrder'] = vm['bootOrder'] else: new_vm['bootOrder'] = ['DISK', 'CDROM'] if not 'hardDrives' in vm: module.fail_json(msg = 'ERROR no hardDrives subsection defined in template!') drives = new_vm['hardDrives'] = [] for hd in vm['hardDrives']: if not 'index' in hd: module.fail_json(msg = 'You must specify an index for all HDs!') if not 'type' in hd: hd['type'] = "DISK" if hd['type'] != "DISK" and hd['type'] != "CDROM": module.fail_json(msg = 'For HD type specify DISK or CDROM!') if not 'controller' in hd: hd['controller'] = "virtio" if hd['controller'] != "virtio" and hd['controller'] != "ide": module.fail_json(msg = 'For HD controller specify virtio or ide!') if not 'boot' in hd: hd['boot'] = False if not 'name' in hd: hd['name'] = "Disk ", hd['index'] new_drive = { 'index': hd['index'], 'type': hd['type'], 'boot': hd['boot'], 'controller': hd['controller'], 'name': hd['name'], } if not 'size' in hd: module.fail_json(msg = 'ERROR HD size not specified for VM!') else: if not 'unit' in hd['size']: module.fail_json(msg = 'ERROR HD size unit not defined') if not 'value' in hd['size']: module.fail_json(msg = 'ERROR HD size value not defined') if hd['size']['unit'] != "GB" and hd['size']['unit'] != "MB": module.fail_json(msg = 'ERROR HD size unit must be GB or MB') if not int(hd['size']['value']): module.fail_json(msg = 'ERROR HD size value must be an int') new_drive['size'] = { 'unit': hd['size']['unit'], 'value': hd['size']['value'] } image = {} if 'baseDiskImageId' in hd: image = get_diskimage(client, hd['baseDiskImageId']) if image is None: module.fail_json(msg = 'FATAL ERROR nonexistent baseDiskImageId %s specified!' % hd['baseDiskImageId']) elif 'imageName' in hd: image = get_diskimage(client, hd['imageName']) if image is None: module.fail_json(msg = 'FATAL ERROR nonexistent imageName %s specified!' % hd['imageName']) if 'baseDiskImageId' in hd or 'imageName' in hd: if hd['size']['value'] < image['size']['value']: module.fail_json(msg = 'ERROR HD size value (%s) is smaller than the image (%s)' % (hd['size']['value'], image['size']['value'])) else: new_drive['baseDiskImageId'] = image['id'] #else: # new_drive['baseDiskImageId'] = 0 drives.append(new_drive) if not 'networkConnections' in vm: module.fail_json(msg = 'FATAL ERROR networkConnections subsection not configured in template!') connections = new_vm['networkConnections'] = [] for nic in vm['networkConnections']: if not 'device' in nic: module.fail_json(msg = 'FATAL ERROR device subsection not configured in networkConnection!') if not 'ipConfig' in nic: module.fail_json(msg = 'FATAL ERROR ipConfig subsection not configured in networkConnection!') if not 'index' in nic['device']: module.fail_json(msg = 'You must specify an index for all NICs!') if not 'name' in nic['device']: nic['device']['name'] = "Nic ", nic['device']['index'] if not 'deviceType' in nic['device']: nic['device']['deviceType'] = "virtio" if nic['device']['deviceType'] != "virtio" and nic['device']['deviceType'] != "e1000": module.fail_json(msg = 'For NIC device deviceType specify virtio or e1000!') new_nic = { 'name': nic['name'] } new_nic['device'] = { 'index': nic['device']['index'], 'deviceType': nic['device']['deviceType'] } if 'useAutomaticMac' in nic['device']: if nic['device']['useAutomaticMac'] == False: new_nic['device']['useAutomaticMac'] = False if 'mac' in nic['device']: new_nic['device']['mac'] = nic['device']['mac'] else: module.fail_json(msg = 'ERROR useAutomaticMac set to False but no static mac set for VM %s NIC index %s!' % (new_vm['name'], new_nic['device']['index'])) else: new_nic['device']['useAutomaticMac'] = True new_nic['ipConfig'] = {} if 'autoIpConfig' in nic['ipConfig']: if 'reservedIp' in nic['ipConfig']['autoIpConfig']: new_nic['ipConfig']['autoIpConfig'] = { 'reservedIp': nic['ipConfig']['autoIpConfig']['reservedIp'] } elif 'staticIpConfig' in nic['ipConfig']: if not 'ip' in nic['ipConfig']['staticIpConfig']: module.fail_json(msg = 'FATAL ERROR ipConfig/staticIpConfig is missing ip!') if not 'mask' in nic['ipConfig']['staticIpConfig']: module.fail_json(msg = 'FATAL ERROR ipConfig/staticIpConfig is missing mask!') new_nic['ipConfig']['staticIpConfig'] = { 'ip': nic['ipConfig']['staticIpConfig']['ip'], 'mask': nic['ipConfig']['staticIpConfig']['mask'] } if 'gateway' in nic['ipConfig']['staticIpConfig']: new_nic['ipConfig']['staticIpConfig']['gateway'] = nic['ipConfig']['staticIpConfig']['gateway'] if 'dns' in nic['ipConfig']['staticIpConfig']: new_nic['ipConfig']['staticIpConfig']['dns'] = nic['ipConfig']['staticIpConfig']['dns'] if 'hasPublicIp' in nic['ipConfig']: new_nic['ipConfig']['hasPublicIp'] = True pubip = True connections.append(new_nic) if pubip and 'suppliedServices' in vm: services = new_vm['suppliedServices'] = [] for svc in vm['suppliedServices']: if not 'name' in svc: module.fail_json(msg = 'FATAL ERROR supplied service missing name!') if not 'ip' in svc: module.fail_json(msg = 'FATAL ERROR supplied service missing ip!') if not 'portRange' in svc: module.fail_json(msg = 'FATAL ERROR supplied service missing portRange!') new_svc = { 'external': True, 'name': svc['name'], 'ip': svc['ip'], 'portRange': svc['portRange'] } if 'protocol' in svc: new_svc['protocol'] = svc['protocol'] services.append(new_svc) new_app['design']['vms'].append(new_vm) try: created_app = client.create_application(new_app) except Exception, e: log_contents = log_capture_string.getvalue() log_capture_string.close() module.fail_json(msg = '%s' % e,stdout='%s' % log_contents, jsonout='%s' % new_app) appID = created_app['id'] blueprint_dict = {"applicationId":appID, "blueprintName":blueprint_name, "offline": False, "description":app_description } try: blueprint_id=((client.create_blueprint(blueprint_dict))['_href'].split('/'))[2] client.delete_application(created_app) module.exit_json(changed=True, app_name='%s' % app_name, blueprint_name='%s' % blueprint_name, blueprint_id='%s' % blueprint_id) except Exception, e: log_contents = log_capture_string.getvalue() log_capture_string.close() module.fail_json(msg = '%s' % e,stdout='%s' % log_contents) def create_app_and_publish(client, module): #validation if not module.params.get("blueprint_id"): module.fail_json(msg='Must supply a blueprint_id', changed=False) if 'performance' == module.params.get("publish_optimization"): if not module.params.get("cloud"): module.fail_json(msg='Must supply a cloud when publish optimization is performance', changed=False) if not module.params.get("region"): module.fail_json(msg='Must supply a region when publish optimization is performance', changed=False) app = {'name': module.params.get("app_name"), 'description': module.params.get("description",''), 'baseBlueprintId': module.params.get("blueprint_id")} app = client.create_application(app) req = {} if 'performance' == module.params.get("publish_optimization"): req = {'id': app['id'] ,'preferredCloud': module.params.get("cloud"),'preferredRegion': module.params.get("region"), 'optimizationLevel': 'PERFORMANCE_OPTIMIZED'} ttl=module.params.get("application_ttl") if ttl != -1: ttl =ttl * 60 exp_req = {'expirationFromNowSeconds': ttl} client.set_application_expiration(app,exp_req) client.publish_application(app, req) _wait_for_state(client,'STARTED',module) log_contents = log_capture_string.getvalue() log_capture_string.close() module.exit_json(changed=True, app_name='%s' % module.params.get("app_name"),stdout='%s' % log_contents, app_id='%s' % app['id']) # import module snippets import ansible import os import functools import logging import io import datetime import sys import yaml import json logger = logging.getLogger() logger.setLevel(logging.DEBUG) log_capture_string = io.BytesIO() from ansible.module_utils.basic import * main() ansible/cloud_providers/ravello/roles/application_create_from_blueprint/tasks/main.yml
New file @@ -0,0 +1,40 @@ --- - name: display variables for create application based on deployed blueprint and wait for start debug: var: "{{ item }}" verbosity: 1 with_items: - application_name - application_description - blueprint_id - wait_timeout - name: create application based on deployed blueprint and wait for start local_action: module: ravello_app app_name: "{{ application_name }}" description: "{{ application_description }}" state: present blueprint_id: "{{ blueprint_id }}" wait_timeout: "{{ wait_timeout }}" register: app_results - debug: var: "{{ item }}" verbosity: 1 with_items: - app_results.app_name - app_results.app_id - name: delete blueprint used to create application local_action: blueprint_name: "{{ design_results.app_name + '-bp' }}" blueprint_id: "{{ design_results.blueprint_id }}" app_name: "{{ app_results.app_name }}" module: ravello_app state: blueprint_delete register: bp_delete_results - debug: var: bp_delete_results verbosity: 1 ansible/cloud_providers/ravello/roles/blueprint_create_from_application/tasks/main.yml
New file @@ -0,0 +1,33 @@ --- - name: display variables for create blueprint from application debug: var: "{{ item }}" verbosity: 1 with_items: - application_name - blueprint_name - blueprint_description - name: stop application local_action: module: ravello_app app_name: "{{ application_name }}" state: stopped register: app_stop_results - debug: var: app_stop_results verbosity: 1 - name: create blueprint from application local_action: module: ravello_app app_name: "{{ application_name }}" blueprint_name: "{{ blueprint_name }}" blueprint_description: "{{ 'blueprint created via ansible from ' + application_name }}" state: blueprint register: bp_create_results - debug: var: bp_create_results verbosity: 1 ansible/cloud_providers/ravello/roles/blueprint_design/tasks/main.yml
New file @@ -0,0 +1,24 @@ --- - name: display variables for create blueprint based on {{ app_template }} debug: var: "{{ item }}" verbosity: 1 with_items: - blueprint_name - blueprint_description - name: create blueprint based on {{ app_template }} local_action: module: ravello_app app_name: "{{ blueprint_name }}" description: "{{ blueprint_description }}" app_template: '{{ app_template}}' state: design register: design_results - debug: var: "{{ item }}" verbosity: 1 with_items: - design_results.name - design_results.blueprint_id ansible/cloud_providers/ravello/roles/warm_up/tasks/main.yml
New file @@ -0,0 +1,24 @@ --- - name: display variables debug: var: "{{ item }}" verbosity: 1 with_items: - unique_name - version - application_name - application_description - blueprint_name - blueprint_id - wait_timeout - name: test login local_action: module: ravello_app app_name: "{{ application_name }}" state: test register: test_results - debug: var: test_results verbosity: 1 ansible/cloud_providers/ravello/save_environment.yml
New file @@ -0,0 +1,12 @@ # Create blueprint from existing app --- - hosts: localhost tasks: - local_action: module: ravello_app name: 'ansible-brett-demo-version1.1-app-v1.0' # app_name: 'brett-demo-version1.1' # state: 'stopped' blueprint_name: 'test-brett-bp-save' blueprint_description: 'Blueprint of app xyz' state: blueprint