#!/usr/bin/python #coding: utf-8 -*- # https://github.com/openstack-ansible/openstack-ansible-modules/blob/master/nova_flavor # (c) 2014, Adam Samalik # # This module 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. # # This software 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 this software. If not, see . try: from novaclient import client from keystoneclient.v2_0 import client as ksclient except ImportError: print("failed=True msg='novaclient and keystone client are required'") DOCUMENTATION = ''' --- module: nova_flavor short_description: Manage OpenStack flavors description: - Create VM flavors with OpenStack Nova service requirements: [ python-novaclient ] options: login_username: description: - user name to authenticate against Identity service required: True aliases: [username] login_password: description: - password to authenticate against Identity service aliases: [password] required: True login_tenant_name: description: - tenant name of the login user aliases: [tenant_name] required: True auth_url: description: - The keystone URL for authentication required: false default: 'http://127.0.0.1:35357/v2.0/' region_name: description: - Name of the region required: False default: None name: description: - Descriptive name of the flavor required: True ram: description: - Memory in MB for the flavor required: False vcpus: description: - Number of VCPUs for the flavor required: False root: description: - Size of root disk in GB required: False ephemeral: description: - Size of ephemeral space in GB required: False swap: description: - Swap space in MB required: False default: 0 id: description: - ID for the flavor (optional). required: False default: ID will be automatically generated is_public: description: - Decide if the flavor is public choices: ['true', 'false'] default: 'true' extra_specs: description: - Metadata used by scheduling to select correct host aggregate default: None state: description: - Create or delete flavor required: False choices: ['present', 'absent'] default: 'present' ''' EXAMPLES = ''' - nova_flavor: login_username: admin login_password: 1234 login_tenant_name: admin name: medium ram: 2048 vcpus: 2 root: 0 ephemeral: 20 ''' def authenticate(module, auth_url, username, password, tenant_name, region): """ Return a Nova client object. """ try: keystone = ksclient.Client(auth_url=auth_url, username=username, password=password, tenant_name=tenant_name, region=region) except Exception as e: module.fail_json( msg = "Could not authenticate with Keystone: {}".format( e.message)) try: nova = client.Client('2', keystone.username, keystone.password, keystone.tenant_name, keystone.auth_url) except Exception as e: module.fail_json(msg = "Could not get Nova client: {}".format( e.message)) return nova def get_flavors(nova, name, id=None): if not id: flavors = [x for x in nova.flavors.list() if x.name == name] else: flavors = [x for x in nova.flavors.list() if x.name == name and x.id == str(id)] return flavors def create_flavor(module, nova, name, ram, vcpus, root, ephemeral, swap, id, is_public, extra_specs): flavors = get_flavors(nova, name, id) if len(flavors) >0: present_flavor = flavors[0] # When flavor is created with 0 swap, nova will use an empty value if present_flavor.swap == "": present_flavor.swap = 0 # flavor create expects lower case is_public values, but converts them into capitalized value if present_flavor.ram == int(ram) and present_flavor.vcpus == int(vcpus) and present_flavor.disk == int(root) and present_flavor.ephemeral == int(ephemeral) and present_flavor.swap == int(swap) and str(present_flavor.is_public) == is_public.capitalize(): if present_flavor.get_keys() == extra_specs: return False, flavors[0].id else: present_flavor.set_keys(extra_specs) return True, flavors[0].id else: module.fail_json(msg = "Flavor {0} already present, but specs have changed!".format(name)) try: flavor = nova.flavors.create(name=name, ram=ram, vcpus=vcpus, disk=root, ephemeral=ephemeral, flavorid=id, swap=swap, is_public=is_public) flavor.set_keys(extra_specs) except Exception as e: module.fail_json(msg = "Could not create a flavor: {0}".format(e.message)) return True, flavor.id def delete_flavor(module, nova, name): flavors = get_flavors(nova, name) if len(flavors) == 0: return False for flavor in flavors: try: nova.flavors.delete(flavor.id); except Exception as e: module.fail_json(msg = "Could not delete flavor {0}: {1}".format(name, e.message)) return True def main(): module = AnsibleModule( argument_spec = dict( login_username = dict(default='admin', aliases=["username"]), login_password = dict(required=True, aliases=["password"]), login_tenant_name = dict(required='True', aliases=["tenant_name"]), auth_url = dict(default='http://127.0.0.1:35357/v2.0/'), region_name = dict(default=None), name = dict(required=True), ram = dict(required=False,default=0), vcpus = dict(required=False,default=0), root = dict(required=False,default=0), ephemeral = dict(required=False,default=0), extra_specs = dict(required=False,default={},type='dict'), swap = dict(default=0), id = dict(default=None), is_public = dict(default='true'), state = dict(default='present'), ) ) auth_url = module.params['auth_url'] region = module.params['region_name'] username = module.params['login_username'] password = module.params['login_password'] tenant_name = module.params['login_tenant_name'] name = module.params['name'] ram = module.params['ram'] vcpus = module.params['vcpus'] root = module.params['root'] ephemeral = module.params['ephemeral'] extra_specs = module.params['extra_specs'] swap = module.params['swap'] id = module.params['id'] state = module.params['state'] is_public = module.params['is_public'] nova = authenticate(module, auth_url, username, password, tenant_name, region) if state == 'present': changed, id = create_flavor(module, nova, name, ram, vcpus, root, ephemeral, swap, id, is_public, extra_specs) module.exit_json(changed=changed, name=name, id=id) elif state == 'absent': changed = delete_flavor(module, nova, name) module.exit_json(changed=changed, name=name) else: raise ValueError("Code should never reach here") # this is magic, see lib/ansible/module_common.py #<> if __name__ == '__main__': main()