-
-
Save evan4498/ec57cf35541f7b646f2a to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/python | |
# This file is part of Ansible | |
# | |
# 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/>. | |
# This is a DOCUMENTATION stub specific to this module, it extends | |
# a documentation fragment located in ansible.utils.module_docs_fragments | |
DOCUMENTATION = ''' | |
--- | |
module: rax | |
short_description: create / delete an instance in Rackspace Public Cloud | |
description: | |
- creates / deletes a Rackspace Public Cloud instance and optionally | |
waits for it to be 'running'. | |
version_added: "1.2" | |
options: | |
auto_increment: | |
description: | |
- Whether or not to increment a single number with the name of the | |
created servers. Only applicable when used with the I(group) attribute | |
or meta key. | |
default: yes | |
version_added: 1.5 | |
count: | |
description: | |
- number of instances to launch | |
default: 1 | |
version_added: 1.4 | |
count_offset: | |
description: | |
- number count to start at | |
default: 1 | |
version_added: 1.4 | |
disk_config: | |
description: | |
- Disk partitioning strategy | |
choices: | |
- auto | |
- manual | |
version_added: '1.4' | |
default: auto | |
exact_count: | |
description: | |
- Explicitly ensure an exact count of instances, used with | |
state=active/present | |
default: no | |
version_added: 1.4 | |
extra_client_args: | |
description: | |
- A hash of key/value pairs to be used when creating the cloudservers | |
client. This is considered an advanced option, use it wisely and | |
with caution. | |
version_added: 1.6 | |
extra_create_args: | |
description: | |
- A hash of key/value pairs to be used when creating a new server. | |
This is considered an advanced option, use it wisely and with caution. | |
version_added: 1.6 | |
files: | |
description: | |
- Files to insert into the instance. remotefilename:localcontent | |
default: null | |
flavor: | |
description: | |
- flavor to use for the instance | |
default: null | |
group: | |
description: | |
- host group to assign to server, is also used for idempotent operations | |
to ensure a specific number of instances | |
version_added: 1.4 | |
image: | |
description: | |
- image to use for the instance. Can be an C(id), C(human_id) or C(name) | |
default: null | |
instance_ids: | |
description: | |
- list of instance ids, currently only used when state='absent' to | |
remove instances | |
version_added: 1.4 | |
key_name: | |
description: | |
- key pair to use on the instance | |
default: null | |
aliases: | |
- keypair | |
meta: | |
description: | |
- A hash of metadata to associate with the instance | |
default: null | |
name: | |
description: | |
- Name to give the instance | |
default: null | |
networks: | |
description: | |
- The network to attach to the instances. If specified, you must include | |
ALL networks including the public and private interfaces. Can be C(id) | |
or C(label). | |
default: | |
- public | |
- private | |
version_added: 1.4 | |
state: | |
description: | |
- Indicate desired state of the resource | |
choices: | |
- present | |
- absent | |
default: present | |
wait: | |
description: | |
- wait for the instance to be in state 'running' before returning | |
default: "no" | |
choices: | |
- "yes" | |
- "no" | |
wait_timeout: | |
description: | |
- how long before wait gives up, in seconds | |
default: 300 | |
author: Jesse Keating, Matt Martz | |
extends_documentation_fragment: rackspace.openstack | |
''' | |
EXAMPLES = ''' | |
- name: Build a Cloud Server | |
gather_facts: False | |
tasks: | |
- name: Server build request | |
local_action: | |
module: rax | |
credentials: ~/.raxpub | |
name: rax-test1 | |
flavor: 5 | |
image: b11d9567-e412-4255-96b9-bd63ab23bcfe | |
files: | |
/root/.ssh/authorized_keys: /home/localuser/.ssh/id_rsa.pub | |
/root/test.txt: /home/localuser/test.txt | |
wait: yes | |
state: present | |
networks: | |
- private | |
- public | |
register: rax | |
- name: Build an exact count of cloud servers with incremented names | |
hosts: local | |
gather_facts: False | |
tasks: | |
- name: Server build requests | |
local_action: | |
module: rax | |
credentials: ~/.raxpub | |
name: test%03d.example.org | |
flavor: performance1-1 | |
image: ubuntu-1204-lts-precise-pangolin | |
state: present | |
count: 10 | |
count_offset: 10 | |
exact_count: yes | |
group: test | |
wait: yes | |
register: rax | |
''' | |
import os | |
import re | |
import time | |
from uuid import UUID | |
from types import NoneType | |
try: | |
import pyrax | |
HAS_PYRAX = True | |
except ImportError: | |
HAS_PYRAX = False | |
ACTIVE_STATUSES = ('ACTIVE', 'BUILD', 'HARD_REBOOT', 'MIGRATING', 'PASSWORD', | |
'REBOOT', 'REBUILD', 'RESCUE', 'RESIZE', 'REVERT_RESIZE') | |
FINAL_STATUSES = ('ACTIVE', 'ERROR') | |
NON_CALLABLES = (basestring, bool, dict, int, list, NoneType) | |
PUBLIC_NET_ID = "00000000-0000-0000-0000-000000000000" | |
SERVICE_NET_ID = "11111111-1111-1111-1111-111111111111" | |
def rax_slugify(value): | |
return 'rax_%s' % (re.sub('[^\w-]', '_', value).lower().lstrip('_')) | |
def pyrax_object_to_dict(obj): | |
instance = {} | |
for key in dir(obj): | |
value = getattr(obj, key) | |
if (isinstance(value, NON_CALLABLES) and not key.startswith('_')): | |
key = rax_slugify(key) | |
instance[key] = value | |
for attr in ['id', 'accessIPv4', 'name', 'status']: | |
instance[attr] = instance.get(rax_slugify(attr)) | |
return instance | |
def create(module, names, flavor, image, meta, key_name, files, | |
wait, wait_timeout, disk_config, group, nics, | |
extra_create_args): | |
cs = pyrax.cloudservers | |
changed = False | |
# Handle the file contents | |
for rpath in files.keys(): | |
lpath = os.path.expanduser(files[rpath]) | |
try: | |
fileobj = open(lpath, 'r') | |
files[rpath] = fileobj.read() | |
except Exception, e: | |
module.fail_json(msg='Failed to load %s' % lpath) | |
try: | |
servers = [] | |
for name in names: | |
servers.append(cs.servers.create(name=name, image=image, | |
flavor=flavor, meta=meta, | |
key_name=key_name, | |
files=files, nics=nics, | |
disk_config=disk_config, | |
**extra_create_args)) | |
except Exception, e: | |
module.fail_json(msg='%s' % e.message) | |
else: | |
changed = True | |
if wait: | |
end_time = time.time() + wait_timeout | |
infinite = wait_timeout == 0 | |
while infinite or time.time() < end_time: | |
for server in servers: | |
try: | |
server.get() | |
except: | |
server.status == 'ERROR' | |
if not filter(lambda s: s.status not in FINAL_STATUSES, | |
servers): | |
break | |
time.sleep(5) | |
success = [] | |
error = [] | |
timeout = [] | |
for server in servers: | |
try: | |
server.get() | |
except: | |
server.status == 'ERROR' | |
instance = pyrax_object_to_dict(server) | |
if server.status == 'ACTIVE' or not wait: | |
success.append(instance) | |
elif server.status == 'ERROR': | |
error.append(instance) | |
elif wait: | |
timeout.append(instance) | |
results = { | |
'changed': changed, | |
'action': 'create', | |
'instances': success + error + timeout, | |
'success': success, | |
'error': error, | |
'timeout': timeout, | |
'instance_ids': { | |
'instances': [i['id'] for i in success + error + timeout], | |
'success': [i['id'] for i in success], | |
'error': [i['id'] for i in error], | |
'timeout': [i['id'] for i in timeout] | |
} | |
} | |
if timeout: | |
results['msg'] = 'Timeout waiting for all servers to build' | |
elif error: | |
results['msg'] = 'Failed to build all servers' | |
if 'msg' in results: | |
module.fail_json(**results) | |
else: | |
module.exit_json(**results) | |
def delete(module, instance_ids, wait, wait_timeout): | |
cs = pyrax.cloudservers | |
changed = False | |
instances = {} | |
servers = [] | |
for instance_id in instance_ids: | |
servers.append(cs.servers.get(instance_id)) | |
for server in servers: | |
try: | |
server.delete() | |
except Exception, e: | |
module.fail_json(msg=e.message) | |
else: | |
changed = True | |
instance = pyrax_object_to_dict(server) | |
instances[instance['id']] = instance | |
# If requested, wait for server deletion | |
if wait: | |
end_time = time.time() + wait_timeout | |
infinite = wait_timeout == 0 | |
while infinite or time.time() < end_time: | |
for server in servers: | |
instance_id = server.id | |
try: | |
server.get() | |
except: | |
instances[instance_id]['status'] = 'DELETED' | |
if not filter(lambda s: s['status'] not in ('', 'DELETED', | |
'ERROR'), | |
instances.values()): | |
break | |
time.sleep(5) | |
timeout = filter(lambda s: s['status'] not in ('', 'DELETED', 'ERROR'), | |
instances.values()) | |
error = filter(lambda s: s['status'] in ('ERROR'), | |
instances.values()) | |
success = filter(lambda s: s['status'] in ('', 'DELETED'), | |
instances.values()) | |
results = { | |
'changed': changed, | |
'action': 'delete', | |
'instances': success + error + timeout, | |
'success': success, | |
'error': error, | |
'timeout': timeout, | |
'instance_ids': { | |
'instances': [i['id'] for i in success + error + timeout], | |
'success': [i['id'] for i in success], | |
'error': [i['id'] for i in error], | |
'timeout': [i['id'] for i in timeout] | |
} | |
} | |
if timeout: | |
results['msg'] = 'Timeout waiting for all servers to delete' | |
elif error: | |
results['msg'] = 'Failed to delete all servers' | |
if 'msg' in results: | |
module.fail_json(**results) | |
else: | |
module.exit_json(**results) | |
def cloudservers(module, state, name, flavor, image, meta, key_name, files, | |
wait, wait_timeout, disk_config, count, group, | |
instance_ids, exact_count, networks, count_offset, | |
auto_increment, extra_create_args): | |
cs = pyrax.cloudservers | |
cnw = pyrax.cloud_networks | |
if not cnw: | |
module.fail_json(msg='Failed to instantiate client. This ' | |
'typically indicates an invalid region or an ' | |
'incorrectly capitalized region name.') | |
servers = [] | |
#for key, value in meta.items(): | |
# meta[key] = repr(value) | |
# Add the group meta key | |
if group and 'group' not in meta: | |
meta['group'] = group | |
elif 'group' in meta and group is None: | |
group = meta['group'] | |
# Normalize and ensure all metadata values are strings | |
for k, v in meta.items(): | |
if isinstance(v, list): | |
meta[k] = ','.join(['%s' % i for i in v]) | |
elif isinstance(v, dict): | |
meta[k] = json.dumps(v) | |
elif not isinstance(v, basestring): | |
meta[k] = '%s' % v | |
# When using state=absent with group, the absent block won't match the | |
# names properly. Use the exact_count functionality to decrease the count | |
# to the desired level | |
was_absent = False | |
if group is not None and state == 'absent': | |
exact_count = True | |
state = 'present' | |
was_absent = True | |
# Check if the provided image is a UUID and if not, search for an | |
# appropriate image using human_id and name | |
if image: | |
try: | |
UUID(image) | |
except ValueError: | |
try: | |
image = cs.images.find(human_id=image) | |
except(cs.exceptions.NotFound, | |
cs.exceptions.NoUniqueMatch): | |
try: | |
image = cs.images.find(name=image) | |
except (cs.exceptions.NotFound, | |
cs.exceptions.NoUniqueMatch): | |
module.fail_json(msg='No matching image found (%s)' % | |
image) | |
image = pyrax.utils.get_id(image) | |
# Check if the provided network is a UUID and if not, search for an | |
# appropriate network using label | |
nics = [] | |
if networks: | |
for network in networks: | |
try: | |
UUID(network) | |
except ValueError: | |
if network.lower() == 'public': | |
nics.extend(cnw.get_server_networks(PUBLIC_NET_ID)) | |
elif network.lower() == 'private': | |
nics.extend(cnw.get_server_networks(SERVICE_NET_ID)) | |
else: | |
try: | |
network_obj = cnw.find_network_by_label(network) | |
except (pyrax.exceptions.NetworkNotFound, | |
pyrax.exceptions.NetworkLabelNotUnique): | |
module.fail_json(msg='No matching network found (%s)' % | |
network) | |
else: | |
nics.extend(cnw.get_server_networks(network_obj)) | |
else: | |
nics.extend(cnw.get_server_networks(network)) | |
# act on the state | |
if state == 'present': | |
for arg, value in dict(name=name, flavor=flavor, | |
image=image).iteritems(): | |
if not value: | |
module.fail_json(msg='%s is required for the "rax" module' % | |
arg) | |
# Idempotent ensurance of a specific count of servers | |
if exact_count is not False: | |
# See if we can find servers that match our options | |
if group is None: | |
module.fail_json(msg='"group" must be provided when using ' | |
'"exact_count"') | |
else: | |
if auto_increment: | |
numbers = set() | |
try: | |
name % 0 | |
except TypeError, e: | |
if e.message.startswith('not all'): | |
name = '%s%%d' % name | |
else: | |
module.fail_json(msg=e.message) | |
pattern = re.sub(r'%\d*[sd]', r'(\d+)', name) | |
for server in cs.servers.list(): | |
if server.metadata.get('group') == group: | |
servers.append(server) | |
match = re.search(pattern, server.name) | |
if match: | |
number = int(match.group(1)) | |
numbers.add(number) | |
number_range = xrange(count_offset, count_offset + count) | |
available_numbers = list(set(number_range) | |
.difference(numbers)) | |
else: | |
for server in cs.servers.list(): | |
if server.metadata.get('group') == group: | |
servers.append(server) | |
# If state was absent but the count was changed, | |
# assume we only wanted to remove that number of instances | |
if was_absent: | |
diff = len(servers) - count | |
if diff < 0: | |
count = 0 | |
else: | |
count = diff | |
if len(servers) > count: | |
state = 'absent' | |
del servers[:count] | |
instance_ids = [] | |
for server in servers: | |
instance_ids.append(server.id) | |
delete(module, instance_ids, wait, wait_timeout) | |
elif len(servers) < count: | |
if auto_increment: | |
names = [] | |
name_slice = count - len(servers) | |
numbers_to_use = available_numbers[:name_slice] | |
for number in numbers_to_use: | |
names.append(name % number) | |
else: | |
names = [name] * (count - len(servers)) | |
else: | |
module.exit_json(changed=False, action=None, instances=[], | |
success=[], error=[], timeout=[], | |
instance_ids={'instances': [], | |
'success': [], 'error': [], | |
'timeout': []}) | |
else: | |
if group is not None: | |
if auto_increment: | |
numbers = set() | |
try: | |
name % 0 | |
except TypeError, e: | |
if e.message.startswith('not all'): | |
name = '%s%%d' % name | |
else: | |
module.fail_json(msg=e.message) | |
pattern = re.sub(r'%\d*[sd]', r'(\d+)', name) | |
for server in cs.servers.list(): | |
if server.metadata.get('group') == group: | |
servers.append(server) | |
match = re.search(pattern, server.name) | |
if match: | |
number = int(match.group(1)) | |
numbers.add(number) | |
number_range = xrange(count_offset, | |
count_offset + count + len(numbers)) | |
available_numbers = list(set(number_range) | |
.difference(numbers)) | |
names = [] | |
numbers_to_use = available_numbers[:count] | |
for number in numbers_to_use: | |
names.append(name % number) | |
else: | |
names = [name] * count | |
else: | |
search_opts = { | |
'name': '^%s$' % name, | |
'image': image, | |
'flavor': flavor | |
} | |
servers = [] | |
for server in cs.servers.list(search_opts=search_opts): | |
if server.metadata != meta: | |
continue | |
servers.append(server) | |
if len(servers) >= count: | |
instances = [] | |
for server in servers: | |
instances.append(pyrax_object_to_dict(server)) | |
instance_ids = [i['id'] for i in instances] | |
module.exit_json(changed=False, action=None, | |
instances=instances, success=[], error=[], | |
timeout=[], | |
instance_ids={'instances': instance_ids, | |
'success': [], 'error': [], | |
'timeout': []}) | |
names = [name] * (count - len(servers)) | |
create(module, names, flavor, image, meta, key_name, files, | |
wait, wait_timeout, disk_config, group, nics, extra_create_args) | |
elif state == 'absent': | |
if instance_ids is None: | |
for arg, value in dict(name=name, flavor=flavor, | |
image=image).iteritems(): | |
if not value: | |
module.fail_json(msg='%s is required for the "rax" ' | |
'module' % arg) | |
search_opts = { | |
'name': '^%s$' % name, | |
'image': image, | |
'flavor': flavor | |
} | |
for server in cs.servers.list(search_opts=search_opts): | |
if meta != server.metadata: | |
continue | |
servers.append(server) | |
instance_ids = [] | |
for server in servers: | |
if len(instance_ids) < count: | |
instance_ids.append(server.id) | |
else: | |
break | |
if not instance_ids: | |
module.exit_json(changed=False, action=None, instances=[], | |
success=[], error=[], timeout=[], | |
instance_ids={'instances': [], | |
'success': [], 'error': [], | |
'timeout': []}) | |
delete(module, instance_ids, wait, wait_timeout) | |
def main(): | |
argument_spec = rax_argument_spec() | |
argument_spec.update( | |
dict( | |
auto_increment=dict(default=True, type='bool'), | |
count=dict(default=1, type='int'), | |
count_offset=dict(default=1, type='int'), | |
disk_config=dict(choices=['auto', 'manual']), | |
exact_count=dict(default=False, type='bool'), | |
extra_client_args=dict(type='dict', default={}), | |
extra_create_args=dict(type='dict', default={}), | |
files=dict(type='dict', default={}), | |
flavor=dict(), | |
group=dict(), | |
image=dict(), | |
instance_ids=dict(type='list'), | |
key_name=dict(aliases=['keypair']), | |
meta=dict(type='dict', default={}), | |
name=dict(), | |
networks=dict(type='list', default=['public', 'private']), | |
service=dict(), | |
state=dict(default='present', choices=['present', 'absent']), | |
wait=dict(default=False, type='bool'), | |
wait_timeout=dict(default=300), | |
) | |
) | |
module = AnsibleModule( | |
argument_spec=argument_spec, | |
required_together=rax_required_together(), | |
) | |
if not HAS_PYRAX: | |
module.fail_json(msg='pyrax is required for this module') | |
service = module.params.get('service') | |
if service is not None: | |
module.fail_json(msg='The "service" attribute has been deprecated, ' | |
'please remove "service: cloudservers" from your ' | |
'playbook pertaining to the "rax" module') | |
auto_increment = module.params.get('auto_increment') | |
count = module.params.get('count') | |
count_offset = module.params.get('count_offset') | |
disk_config = module.params.get('disk_config') | |
if disk_config: | |
disk_config = disk_config.upper() | |
exact_count = module.params.get('exact_count', False) | |
extra_client_args = module.params.get('extra_client_args') | |
extra_create_args = module.params.get('extra_create_args') | |
files = module.params.get('files') | |
flavor = module.params.get('flavor') | |
group = module.params.get('group') | |
image = module.params.get('image') | |
instance_ids = module.params.get('instance_ids') | |
key_name = module.params.get('key_name') | |
meta = module.params.get('meta') | |
name = module.params.get('name') | |
networks = module.params.get('networks') | |
state = module.params.get('state') | |
wait = module.params.get('wait') | |
wait_timeout = int(module.params.get('wait_timeout')) | |
setup_rax_module(module, pyrax) | |
if extra_client_args: | |
pyrax.cloudservers = pyrax.connect_to_cloudservers( | |
region=pyrax.cloudservers.client.region_name, | |
**extra_client_args) | |
client = pyrax.cloudservers.client | |
if 'bypass_url' in extra_client_args: | |
client.management_url = extra_client_args['bypass_url'] | |
if pyrax.cloudservers is None: | |
module.fail_json(msg='Failed to instantiate client. This ' | |
'typically indicates an invalid region or an ' | |
'incorrectly capitalized region name.') | |
cloudservers(module, state, name, flavor, image, meta, key_name, files, | |
wait, wait_timeout, disk_config, count, group, | |
instance_ids, exact_count, networks, count_offset, | |
auto_increment, extra_create_args) | |
# import module snippets | |
from ansible.module_utils.basic import * | |
from ansible.module_utils.rax import * | |
### invoke the module | |
main() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/python | |
# This file is part of Ansible | |
# | |
# 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/>. | |
# This is a DOCUMENTATION stub specific to this module, it extends | |
# a documentation fragment located in ansible.utils.module_docs_fragments | |
DOCUMENTATION = ''' | |
--- | |
module: rax_meta | |
short_description: Manipulate metadata for Rackspace Cloud Servers | |
description: | |
- Manipulate metadata for Rackspace Cloud Servers | |
version_added: 1.6 | |
options: | |
address: | |
description: | |
- Server IP address to modify metadata for, will match any IP assigned to | |
the server | |
id: | |
description: | |
- Server ID to modify metadata for | |
name: | |
description: | |
- Server name to modify metadata for | |
default: null | |
meta: | |
description: | |
- A hash of metadata to associate with the instance | |
default: null | |
author: Matt Martz | |
extends_documentation_fragment: rackspace.openstack | |
''' | |
EXAMPLES = ''' | |
- name: Set metadata for a server | |
hosts: all | |
gather_facts: False | |
tasks: | |
- name: Set metadata | |
local_action: | |
module: rax_meta | |
credentials: ~/.raxpub | |
name: "{{ inventory_hostname }}" | |
region: DFW | |
meta: | |
group: primary_group | |
groups: | |
- group_two | |
- group_three | |
app: my_app | |
- name: Clear metadata | |
local_action: | |
module: rax_meta | |
credentials: ~/.raxpub | |
name: "{{ inventory_hostname }}" | |
region: DFW | |
''' | |
try: | |
import pyrax | |
HAS_PYRAX = True | |
except ImportError: | |
HAS_PYRAX = False | |
def rax_meta(module, address, name, server_id, meta): | |
changed = False | |
cs = pyrax.cloudservers | |
if cs is None: | |
module.fail_json(msg='Failed to instantiate client. This ' | |
'typically indicates an invalid region or an ' | |
'incorrectly capitalized region name.') | |
search_opts = {} | |
if name: | |
search_opts = dict(name='^%s$' % name) | |
try: | |
servers = cs.servers.list(search_opts=search_opts) | |
except Exception, e: | |
module.fail_json(msg='%s' % e.message) | |
elif address: | |
servers = [] | |
try: | |
for server in cs.servers.list(): | |
for addresses in server.networks.values(): | |
if address in addresses: | |
servers.append(server) | |
break | |
except Exception, e: | |
module.fail_json(msg='%s' % e.message) | |
elif server_id: | |
servers = [] | |
try: | |
servers.append(cs.servers.get(server_id)) | |
except Exception, e: | |
pass | |
if len(servers) > 1: | |
module.fail_json(msg='Multiple servers found matching provided ' | |
'search parameters') | |
elif not servers: | |
module.fail_json(msg='Failed to find a server matching provided ' | |
'search parameters') | |
# Normalize and ensure all metadata values are strings | |
for k, v in meta.items(): | |
if isinstance(v, list): | |
meta[k] = ','.join(['%s' % i for i in v]) | |
elif isinstance(v, dict): | |
meta[k] = json.dumps(v) | |
elif not isinstance(v, basestring): | |
meta[k] = '%s' % v | |
server = servers[0] | |
if server.metadata == meta: | |
changed = False | |
else: | |
changed = True | |
removed = set(server.metadata.keys()).difference(meta.keys()) | |
cs.servers.delete_meta(server, list(removed)) | |
cs.servers.set_meta(server, meta) | |
server.get() | |
module.exit_json(changed=changed, meta=server.metadata) | |
def main(): | |
argument_spec = rax_argument_spec() | |
argument_spec.update( | |
dict( | |
address=dict(), | |
id=dict(), | |
name=dict(), | |
meta=dict(type='dict', default=dict()), | |
) | |
) | |
module = AnsibleModule( | |
argument_spec=argument_spec, | |
required_together=rax_required_together(), | |
mutually_exclusive=[['address', 'id', 'name']], | |
required_one_of=[['address', 'id', 'name']], | |
) | |
if not HAS_PYRAX: | |
module.fail_json(msg='pyrax is required for this module') | |
address = module.params.get('address') | |
server_id = module.params.get('id') | |
name = module.params.get('name') | |
meta = module.params.get('meta') | |
setup_rax_module(module, pyrax) | |
rax_meta(module, address, name, server_id, meta) | |
# import module snippets | |
from ansible.module_utils.basic import * | |
from ansible.module_utils.rax import * | |
### invoke the module | |
main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment