1#!/usr/local/bin/python3.8 2# -*- coding: utf-8 -*- 3# Copyright: Ansible Project 4# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) 5 6from __future__ import absolute_import, division, print_function 7__metaclass__ = type 8 9 10DOCUMENTATION = ''' 11--- 12module: rax_meta 13short_description: Manipulate metadata for Rackspace Cloud Servers 14description: 15 - Manipulate metadata for Rackspace Cloud Servers 16options: 17 address: 18 type: str 19 description: 20 - Server IP address to modify metadata for, will match any IP assigned to 21 the server 22 id: 23 type: str 24 description: 25 - Server ID to modify metadata for 26 name: 27 type: str 28 description: 29 - Server name to modify metadata for 30 meta: 31 type: dict 32 description: 33 - A hash of metadata to associate with the instance 34author: "Matt Martz (@sivel)" 35extends_documentation_fragment: 36- community.general.rackspace.openstack 37 38''' 39 40EXAMPLES = ''' 41- name: Set metadata for a server 42 hosts: all 43 gather_facts: False 44 tasks: 45 - name: Set metadata 46 local_action: 47 module: rax_meta 48 credentials: ~/.raxpub 49 name: "{{ inventory_hostname }}" 50 region: DFW 51 meta: 52 group: primary_group 53 groups: 54 - group_two 55 - group_three 56 app: my_app 57 58 - name: Clear metadata 59 local_action: 60 module: rax_meta 61 credentials: ~/.raxpub 62 name: "{{ inventory_hostname }}" 63 region: DFW 64''' 65 66import json 67 68try: 69 import pyrax 70 HAS_PYRAX = True 71except ImportError: 72 HAS_PYRAX = False 73 74from ansible.module_utils.basic import AnsibleModule 75from ansible_collections.community.general.plugins.module_utils.rax import rax_argument_spec, rax_required_together, setup_rax_module 76from ansible.module_utils.six import string_types 77 78 79def rax_meta(module, address, name, server_id, meta): 80 changed = False 81 82 cs = pyrax.cloudservers 83 84 if cs is None: 85 module.fail_json(msg='Failed to instantiate client. This ' 86 'typically indicates an invalid region or an ' 87 'incorrectly capitalized region name.') 88 89 search_opts = {} 90 if name: 91 search_opts = dict(name='^%s$' % name) 92 try: 93 servers = cs.servers.list(search_opts=search_opts) 94 except Exception as e: 95 module.fail_json(msg='%s' % e.message) 96 elif address: 97 servers = [] 98 try: 99 for server in cs.servers.list(): 100 for addresses in server.networks.values(): 101 if address in addresses: 102 servers.append(server) 103 break 104 except Exception as e: 105 module.fail_json(msg='%s' % e.message) 106 elif server_id: 107 servers = [] 108 try: 109 servers.append(cs.servers.get(server_id)) 110 except Exception as e: 111 pass 112 113 if len(servers) > 1: 114 module.fail_json(msg='Multiple servers found matching provided ' 115 'search parameters') 116 elif not servers: 117 module.fail_json(msg='Failed to find a server matching provided ' 118 'search parameters') 119 120 # Normalize and ensure all metadata values are strings 121 for k, v in meta.items(): 122 if isinstance(v, list): 123 meta[k] = ','.join(['%s' % i for i in v]) 124 elif isinstance(v, dict): 125 meta[k] = json.dumps(v) 126 elif not isinstance(v, string_types): 127 meta[k] = '%s' % v 128 129 server = servers[0] 130 if server.metadata == meta: 131 changed = False 132 else: 133 changed = True 134 removed = set(server.metadata.keys()).difference(meta.keys()) 135 cs.servers.delete_meta(server, list(removed)) 136 cs.servers.set_meta(server, meta) 137 server.get() 138 139 module.exit_json(changed=changed, meta=server.metadata) 140 141 142def main(): 143 argument_spec = rax_argument_spec() 144 argument_spec.update( 145 dict( 146 address=dict(), 147 id=dict(), 148 name=dict(), 149 meta=dict(type='dict', default=dict()), 150 ) 151 ) 152 153 module = AnsibleModule( 154 argument_spec=argument_spec, 155 required_together=rax_required_together(), 156 mutually_exclusive=[['address', 'id', 'name']], 157 required_one_of=[['address', 'id', 'name']], 158 ) 159 160 if not HAS_PYRAX: 161 module.fail_json(msg='pyrax is required for this module') 162 163 address = module.params.get('address') 164 server_id = module.params.get('id') 165 name = module.params.get('name') 166 meta = module.params.get('meta') 167 168 setup_rax_module(module, pyrax) 169 170 rax_meta(module, address, name, server_id, meta) 171 172 173if __name__ == '__main__': 174 main() 175