mirror of
https://github.com/ansible-collections/community.general.git
synced 2024-09-14 20:13:21 +02:00
Add OpenStack Server module
Also deprecated old nova_compute module
This commit is contained in:
parent
118382ca70
commit
78daf52994
2 changed files with 449 additions and 0 deletions
|
@ -33,6 +33,7 @@ DOCUMENTATION = '''
|
|||
---
|
||||
module: nova_compute
|
||||
version_added: "1.2"
|
||||
deprecated: Deprecated in 1.10. Use os_server instead
|
||||
short_description: Create/Delete VMs from OpenStack
|
||||
description:
|
||||
- Create or Remove virtual machines from Openstack.
|
448
lib/ansible/modules/cloud/openstack/os_server.py
Normal file
448
lib/ansible/modules/cloud/openstack/os_server.py
Normal file
|
@ -0,0 +1,448 @@
|
|||
#!/usr/bin/python
|
||||
# coding: utf-8 -*-
|
||||
|
||||
# Copyright (c) 2014 Hewlett-Packard Development Company, L.P.
|
||||
# Copyright (c) 2013, Benno Joy <benno@ansible.com>
|
||||
# Copyright (c) 2013, John Dewey <john@dewey.ws>
|
||||
#
|
||||
# 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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
|
||||
try:
|
||||
import shade
|
||||
from shade import meta
|
||||
HAS_SHADE = True
|
||||
except ImportError:
|
||||
HAS_SHADE = False
|
||||
|
||||
|
||||
DOCUMENTATION = '''
|
||||
---
|
||||
module: os_server
|
||||
short_description: Create/Delete Compute Instances from OpenStack
|
||||
extends_documentation_fragment: openstack
|
||||
version_added: "1.10"
|
||||
description:
|
||||
- Create or Remove compute instances from OpenStack.
|
||||
options:
|
||||
name:
|
||||
description:
|
||||
- Name that has to be given to the instance
|
||||
required: true
|
||||
default: None
|
||||
image:
|
||||
description:
|
||||
- The name or id of the base image to boot.
|
||||
required: true
|
||||
default: None
|
||||
image_exclude:
|
||||
description:
|
||||
- Text to use to filter image names, for the case, such as HP, where
|
||||
there are multiple image names matching the common identifying
|
||||
portions. image_exclude is a negative match filter - it is text that
|
||||
may not exist in the image name. Defaults to "(deprecated)"
|
||||
flavor:
|
||||
description:
|
||||
- The name or id of the flavor in which the new instance has to be
|
||||
created. Mutually exclusive with flavor_ram
|
||||
required: false
|
||||
default: 1
|
||||
flavor_ram:
|
||||
description:
|
||||
- The minimum amount of ram in MB that the flavor in which the new
|
||||
instance has to be created must have. Mutually exclusive with flavor.
|
||||
required: false
|
||||
default: 1
|
||||
flavor_include:
|
||||
description:
|
||||
- Text to use to filter flavor names, for the case, such as Rackspace,
|
||||
where there are multiple flavors that have the same ram count.
|
||||
flavor_include is a positive match filter - it must exist in the
|
||||
flavor name.
|
||||
key_name:
|
||||
description:
|
||||
- The key pair name to be used when creating a instance
|
||||
required: false
|
||||
default: None
|
||||
security_groups:
|
||||
description:
|
||||
- The name of the security group to which the instance should be added
|
||||
required: false
|
||||
default: None
|
||||
nics:
|
||||
description:
|
||||
- A list of networks to which the instance's interface should
|
||||
be attached. Networks may be referenced by net-id or net-name.
|
||||
required: false
|
||||
default: None
|
||||
public_ip:
|
||||
description:
|
||||
- Ensure instance has public ip however the cloud wants to do that
|
||||
required: false
|
||||
default: 'yes'
|
||||
floating_ips:
|
||||
decription:
|
||||
- list of valid floating IPs that pre-exist to assign to this node
|
||||
required: false
|
||||
default: None
|
||||
floating_ip_pools:
|
||||
description:
|
||||
- list of floating IP pools from which to choose a floating IP
|
||||
required: false
|
||||
default: None
|
||||
meta:
|
||||
description:
|
||||
- A list of key value pairs that should be provided as a metadata to
|
||||
the new instance.
|
||||
required: false
|
||||
default: None
|
||||
wait:
|
||||
description:
|
||||
- If the module should wait for the instance to be created.
|
||||
required: false
|
||||
default: 'yes'
|
||||
timeout:
|
||||
description:
|
||||
- The amount of time the module should wait for the instance to get
|
||||
into active state.
|
||||
required: false
|
||||
default: 180
|
||||
config_drive:
|
||||
description:
|
||||
- Whether to boot the server with config drive enabled
|
||||
required: false
|
||||
default: 'no'
|
||||
userdata:
|
||||
description:
|
||||
- Opaque blob of data which is made available to the instance
|
||||
required: false
|
||||
default: None
|
||||
root_volume:
|
||||
description:
|
||||
- Boot instance from a volume
|
||||
required: false
|
||||
default: None
|
||||
terminate_volume:
|
||||
description:
|
||||
- If true, delete volume when deleting instance (if booted from volume)
|
||||
default: false
|
||||
state:
|
||||
description:
|
||||
- Should the resource be present or absent.
|
||||
choices: [present, absent]
|
||||
default: present
|
||||
requirements: ["shade"]
|
||||
'''
|
||||
|
||||
EXAMPLES = '''
|
||||
# Creates a new instance and attaches to a network and passes metadata to
|
||||
# the instance
|
||||
- os_server:
|
||||
state: present
|
||||
auth:
|
||||
auth_url: https://region-b.geo-1.identity.hpcloudsvc.com:35357/v2.0/
|
||||
username: admin
|
||||
password: admin
|
||||
project_name: admin
|
||||
name: vm1
|
||||
image: 4f905f38-e52a-43d2-b6ec-754a13ffb529
|
||||
key_name: ansible_key
|
||||
timeout: 200
|
||||
flavor: 4
|
||||
nics:
|
||||
- net-id: 34605f38-e52a-25d2-b6ec-754a13ffb723
|
||||
- net-name: another_network
|
||||
meta:
|
||||
hostname: test1
|
||||
group: uge_master
|
||||
|
||||
# Creates a new instance in HP Cloud AE1 region availability zone az2 and
|
||||
# automatically assigns a floating IP
|
||||
- name: launch a compute instance
|
||||
hosts: localhost
|
||||
tasks:
|
||||
- name: launch an instance
|
||||
os_server:
|
||||
state: present
|
||||
auth:
|
||||
auth_url: https://region-b.geo-1.identity.hpcloudsvc.com:35357/v2.0/
|
||||
username: username
|
||||
password: Equality7-2521
|
||||
project_name: username-project1
|
||||
name: vm1
|
||||
region_name: region-b.geo-1
|
||||
availability_zone: az2
|
||||
image: 9302692b-b787-4b52-a3a6-daebb79cb498
|
||||
key_name: test
|
||||
timeout: 200
|
||||
flavor: 101
|
||||
security_groups: default
|
||||
auto_floating_ip: yes
|
||||
|
||||
# Creates a new instance in named cloud mordred availability zone az2
|
||||
# and assigns a pre-known floating IP
|
||||
- name: launch a compute instance
|
||||
hosts: localhost
|
||||
tasks:
|
||||
- name: launch an instance
|
||||
os_server:
|
||||
state: present
|
||||
cloud: mordred
|
||||
name: vm1
|
||||
availability_zone: az2
|
||||
image: 9302692b-b787-4b52-a3a6-daebb79cb498
|
||||
key_name: test
|
||||
timeout: 200
|
||||
flavor: 101
|
||||
floating-ips:
|
||||
- 12.34.56.79
|
||||
|
||||
# Creates a new instance with 4G of RAM on Ubuntu Trusty, ignoring
|
||||
# deprecated images
|
||||
- name: launch a compute instance
|
||||
hosts: localhost
|
||||
tasks:
|
||||
- name: launch an instance
|
||||
os_server:
|
||||
name: vm1
|
||||
state: present
|
||||
cloud: mordred
|
||||
region_name: region-b.geo-1
|
||||
image: Ubuntu Server 14.04
|
||||
image_exclude: deprecated
|
||||
flavor_ram: 4096
|
||||
|
||||
# Creates a new instance with 4G of RAM on Ubuntu Trusty on a Performance node
|
||||
- name: launch a compute instance
|
||||
hosts: localhost
|
||||
tasks:
|
||||
- name: launch an instance
|
||||
os_server:
|
||||
name: vm1
|
||||
cloud: rax-dfw
|
||||
state: present
|
||||
image: Ubuntu 14.04 LTS (Trusty Tahr) (PVHVM)
|
||||
flavor_ram: 4096
|
||||
flavor_include: Performance
|
||||
'''
|
||||
|
||||
|
||||
def _exit_hostvars(module, cloud, server, changed=True):
|
||||
hostvars = meta.get_hostvars_from_server(cloud, server)
|
||||
module.exit_json(changed=changed, id=server.id, openstack=hostvars)
|
||||
|
||||
|
||||
def _network_args(module, cloud):
|
||||
args = []
|
||||
for net in module.params['nics']:
|
||||
if net.get('net-id'):
|
||||
args.append(net)
|
||||
elif net.get('net-name'):
|
||||
by_name = cloud.get_network(net['net-name'])
|
||||
if not by_name:
|
||||
module.fail_json(
|
||||
msg='Could not find network by net-name: %s' %
|
||||
net['net-name'])
|
||||
args.append({'net-id': by_name['id']})
|
||||
return args
|
||||
|
||||
|
||||
def _delete_server(module, cloud):
|
||||
try:
|
||||
cloud.delete_server(
|
||||
module.params['name'], wait=module.params['wait'],
|
||||
timeout=module.params['timeout'])
|
||||
except Exception as e:
|
||||
module.fail_json(msg="Error in deleting vm: %s" % e.message)
|
||||
module.exit_json(changed=True, result='deleted')
|
||||
|
||||
|
||||
def _create_server(module, cloud):
|
||||
flavor = module.params['flavor']
|
||||
flavor_ram = module.params['flavor_ram']
|
||||
flavor_include = module.params['flavor_include']
|
||||
|
||||
image_id = None
|
||||
if not module.params['root_volume']:
|
||||
image_id = cloud.get_image_id(
|
||||
module.params['image'], module.params['image_exclude'])
|
||||
|
||||
if flavor:
|
||||
flavor_id = cloud.get_flavor(flavor)
|
||||
else:
|
||||
flavor_id = cloud.get_flavor_by_ram(flavor_ram, flavor_include)
|
||||
|
||||
nics = _network_args(module, cloud)
|
||||
|
||||
bootkwargs = dict(
|
||||
name=module.params['name'],
|
||||
image=image_id,
|
||||
flavor=flavor_id,
|
||||
nics=nics,
|
||||
meta=module.params['meta'],
|
||||
security_groups=module.params['security_groups'].split(','),
|
||||
userdata=module.params['userdata'],
|
||||
config_drive=module.params['config_drive'],
|
||||
)
|
||||
for optional_param in ('region_name', 'key_name', 'availability_zone'):
|
||||
if module.params[optional_param]:
|
||||
bootkwargs[optional_param] = module.params[optional_param]
|
||||
|
||||
server = cloud.create_server(
|
||||
ip_pool=module.params['floating_ip_pools'],
|
||||
ips=module.params['floating_ips'],
|
||||
auto_ip=module.params['auto_floating_ip'],
|
||||
root_volume=module.params['root_volume'],
|
||||
terminate_volume=module.params['terminate_volume'],
|
||||
wait=module.params['wait'], timeout=module.params['timeout'],
|
||||
**bootkwargs
|
||||
)
|
||||
|
||||
_exit_hostvars(module, cloud, server)
|
||||
|
||||
|
||||
def _delete_floating_ip_list(cloud, server, extra_ips):
|
||||
for ip in extra_ips:
|
||||
cloud.nova_client.servers.remove_floating_ip(
|
||||
server=server.id, address=ip)
|
||||
|
||||
|
||||
def _check_floating_ips(module, cloud, server):
|
||||
changed = False
|
||||
|
||||
auto_floating_ip = module.params['auto_floating_ip']
|
||||
floating_ips = module.params['floating_ips']
|
||||
floating_ip_pools = module.params['floating_ip_pools']
|
||||
|
||||
if floating_ip_pools or floating_ips or auto_floating_ip:
|
||||
ips = openstack_find_nova_addresses(server.addresses, 'floating')
|
||||
if not ips:
|
||||
# If we're configured to have a floating but we don't have one,
|
||||
# let's add one
|
||||
server = cloud.add_ips_to_server(
|
||||
server,
|
||||
auto_ip=auto_floating_ip,
|
||||
ips=floating_ips,
|
||||
ip_pool=floating_ip_pools,
|
||||
)
|
||||
changed = True
|
||||
elif floating_ips:
|
||||
# we were configured to have specific ips, let's make sure we have
|
||||
# those
|
||||
missing_ips = []
|
||||
for ip in floating_ips:
|
||||
if ip not in ips:
|
||||
missing_ips.append(ip)
|
||||
if missing_ips:
|
||||
server = cloud.add_ip_list(server, missing_ips)
|
||||
changed = True
|
||||
extra_ips = []
|
||||
for ip in ips:
|
||||
if ip not in floating_ips:
|
||||
extra_ips.append(ip)
|
||||
if extra_ips:
|
||||
_delete_floating_ip_list(cloud, server, extra_ips)
|
||||
changed = True
|
||||
return (changed, server)
|
||||
|
||||
|
||||
def _get_server_state(module, cloud):
|
||||
state = module.params['state']
|
||||
server = cloud.get_server(module.params['name'])
|
||||
if server and state == 'present':
|
||||
if server.status != 'ACTIVE':
|
||||
module.fail_json(
|
||||
msg="The instance is available but not Active state: "
|
||||
+ server.status)
|
||||
(ip_changed, server) = _check_floating_ips(module, cloud, server)
|
||||
_exit_hostvars(module, cloud, server, ip_changed)
|
||||
if server and state == 'absent':
|
||||
return True
|
||||
if state == 'absent':
|
||||
module.exit_json(changed=False, result="not present")
|
||||
return True
|
||||
|
||||
|
||||
def main():
|
||||
|
||||
argument_spec = openstack_full_argument_spec(
|
||||
name = dict(required=True),
|
||||
image = dict(default=None),
|
||||
image_exclude = dict(default='(deprecated)'),
|
||||
flavor = dict(default=None),
|
||||
flavor_ram = dict(default=None, type='int'),
|
||||
flavor_include = dict(default=None),
|
||||
key_name = dict(default=None),
|
||||
security_groups = dict(default='default'),
|
||||
nics = dict(default=[]),
|
||||
meta = dict(default=None),
|
||||
userdata = dict(default=None),
|
||||
config_drive = dict(default=False, type='bool'),
|
||||
auto_floating_ip = dict(default=True, type='bool'),
|
||||
floating_ips = dict(default=None),
|
||||
floating_ip_pools = dict(default=None),
|
||||
root_volume = dict(default=None),
|
||||
terminate_volume = dict(default=False, type='bool'),
|
||||
state = dict(default='present', choices=['absent', 'present']),
|
||||
)
|
||||
module_kwargs = openstack_module_kwargs(
|
||||
mutually_exclusive=[
|
||||
['auto_floating_ip', 'floating_ips'],
|
||||
['auto_floating_ip', 'floating_ip_pools'],
|
||||
['floating_ips', 'floating_ip_pools'],
|
||||
['flavor', 'flavor_ram'],
|
||||
['image', 'root_volume'],
|
||||
],
|
||||
)
|
||||
module = AnsibleModule(argument_spec, **module_kwargs)
|
||||
|
||||
if not HAS_SHADE:
|
||||
module.fail_json(msg='shade is required for this module')
|
||||
|
||||
state = module.params['state']
|
||||
image = module.params['image']
|
||||
root_volume = module.params['root_volume']
|
||||
flavor = module.params['flavor']
|
||||
flavor_ram = module.params['flavor_ram']
|
||||
|
||||
if state == 'present':
|
||||
if not (image or root_volume):
|
||||
module.fail_json(
|
||||
msg="Parameter 'image' or 'root_volume' is required "
|
||||
"if state == 'present'"
|
||||
)
|
||||
if not flavor and not flavor_ram:
|
||||
module.fail_json(
|
||||
msg="Parameter 'flavor' or 'flavor_ram' is required "
|
||||
"if state == 'present'"
|
||||
)
|
||||
|
||||
try:
|
||||
cloud_params = dict(module.params)
|
||||
cloud_params.pop('userdata', None)
|
||||
cloud = shade.openstack_cloud(**cloud_params)
|
||||
|
||||
if state == 'present':
|
||||
_get_server_state(module, cloud)
|
||||
_create_server(module, cloud)
|
||||
elif state == 'absent':
|
||||
_get_server_state(module, cloud)
|
||||
_delete_server(module, cloud)
|
||||
except shade.OpenStackCloudException as e:
|
||||
module.fail_json(msg=e.message, extra_data=e.extra_data)
|
||||
|
||||
# this is magic, see lib/ansible/module_common.py
|
||||
from ansible.module_utils.basic import *
|
||||
from ansible.module_utils.openstack import *
|
||||
main()
|
Loading…
Reference in a new issue