1#!/usr/local/bin/python3.8 2# -*- coding: utf-8 -*- 3 4# Copyright: (c) 2019, Nurfet Becirevic <nurfet.becirevic@gmail.com> 5# Copyright: (c) 2019, Tomas Karasek <tom.to.the.k@gmail.com> 6# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) 7 8from __future__ import absolute_import, division, print_function 9 10__metaclass__ = type 11 12DOCUMENTATION = ''' 13--- 14module: packet_project 15 16short_description: Create/delete a project in Packet host. 17 18description: 19 - Create/delete a project in Packet host. 20 - API is documented at U(https://www.packet.com/developers/api/#projects). 21 22version_added: '0.2.0' 23 24author: 25 - Tomas Karasek (@t0mk) <tom.to.the.k@gmail.com> 26 - Nurfet Becirevic (@nurfet-becirevic) <nurfet.becirevic@gmail.com> 27 28options: 29 state: 30 description: 31 - Indicate desired state of the target. 32 default: present 33 choices: ['present', 'absent'] 34 type: str 35 36 payment_method: 37 description: 38 - Payment method is name of one of the payment methods available to your user. 39 - When blank, the API assumes the default payment method. 40 type: str 41 42 auth_token: 43 description: 44 - Packet api token. You can also supply it in env var C(PACKET_API_TOKEN). 45 type: str 46 47 name: 48 description: 49 - Name for/of the project. 50 type: str 51 52 org_id: 53 description: 54 - UUID of the organization to create a project for. 55 - When blank, the API assumes the default organization. 56 type: str 57 58 id: 59 description: 60 - UUID of the project which you want to remove. 61 type: str 62 63 custom_data: 64 description: 65 - Custom data about the project to create. 66 type: str 67 68requirements: 69 - "python >= 2.6" 70 - "packet-python >= 1.40" 71 72''' 73 74EXAMPLES = ''' 75# All the examples assume that you have your Packet API token in env var PACKET_API_TOKEN. 76# You can also pass the api token in module param auth_token. 77 78- name: Create new project 79 hosts: localhost 80 tasks: 81 community.general.packet_project: 82 name: "new project" 83 84- name: Create new project within non-default organization 85 hosts: localhost 86 tasks: 87 community.general.packet_project: 88 name: "my org project" 89 org_id: a4cc87f9-e00f-48c2-9460-74aa60beb6b0 90 91- name: Remove project by id 92 hosts: localhost 93 tasks: 94 community.general.packet_project: 95 state: absent 96 id: eef49903-7a09-4ca1-af67-4087c29ab5b6 97 98- name: Create new project with non-default billing method 99 hosts: localhost 100 tasks: 101 community.general.packet_project: 102 name: "newer project" 103 payment_method: "the other visa" 104''' 105 106RETURN = ''' 107changed: 108 description: True if a project was created or removed. 109 type: bool 110 sample: True 111 returned: success 112 113name: 114 description: Name of addressed project. 115 type: str 116 returned: success 117 118id: 119 description: UUID of addressed project. 120 type: str 121 returned: success 122''' 123 124from ansible.module_utils.basic import AnsibleModule, env_fallback 125from ansible.module_utils.common.text.converters import to_native 126 127HAS_PACKET_SDK = True 128 129try: 130 import packet 131except ImportError: 132 HAS_PACKET_SDK = False 133 134PACKET_API_TOKEN_ENV_VAR = "PACKET_API_TOKEN" 135 136 137def act_on_project(target_state, module, packet_conn): 138 result_dict = {'changed': False} 139 given_id = module.params.get('id') 140 given_name = module.params.get('name') 141 if given_id: 142 matching_projects = [ 143 p for p in packet_conn.list_projects() if given_id == p.id] 144 else: 145 matching_projects = [ 146 p for p in packet_conn.list_projects() if given_name == p.name] 147 148 if target_state == 'present': 149 if len(matching_projects) == 0: 150 org_id = module.params.get('org_id') 151 custom_data = module.params.get('custom_data') 152 payment_method = module.params.get('payment_method') 153 154 if not org_id: 155 params = { 156 "name": given_name, 157 "payment_method_id": payment_method, 158 "customdata": custom_data 159 } 160 new_project_data = packet_conn.call_api("projects", "POST", params) 161 new_project = packet.Project(new_project_data, packet_conn) 162 else: 163 new_project = packet_conn.create_organization_project( 164 org_id=org_id, 165 name=given_name, 166 payment_method_id=payment_method, 167 customdata=custom_data 168 ) 169 170 result_dict['changed'] = True 171 matching_projects.append(new_project) 172 173 result_dict['name'] = matching_projects[0].name 174 result_dict['id'] = matching_projects[0].id 175 else: 176 if len(matching_projects) > 1: 177 _msg = ("More than projects matched for module call with state = absent: " 178 "{0}".format(to_native(matching_projects))) 179 module.fail_json(msg=_msg) 180 181 if len(matching_projects) == 1: 182 p = matching_projects[0] 183 result_dict['name'] = p.name 184 result_dict['id'] = p.id 185 result_dict['changed'] = True 186 try: 187 p.delete() 188 except Exception as e: 189 _msg = ("while trying to remove project {0}, id {1}, got error: {2}".format( 190 p.name, p.id, to_native(e))) 191 module.fail_json(msg=_msg) 192 return result_dict 193 194 195def main(): 196 module = AnsibleModule( 197 argument_spec=dict( 198 state=dict(choices=['present', 'absent'], default='present'), 199 auth_token=dict( 200 type='str', 201 fallback=(env_fallback, [PACKET_API_TOKEN_ENV_VAR]), 202 no_log=True 203 ), 204 name=dict(type='str'), 205 id=dict(type='str'), 206 org_id=dict(type='str'), 207 payment_method=dict(type='str'), 208 custom_data=dict(type='str'), 209 ), 210 supports_check_mode=True, 211 required_one_of=[("name", "id",)], 212 mutually_exclusive=[ 213 ('name', 'id'), 214 ] 215 ) 216 if not HAS_PACKET_SDK: 217 module.fail_json(msg='packet required for this module') 218 219 if not module.params.get('auth_token'): 220 _fail_msg = ("if Packet API token is not in environment variable {0}, " 221 "the auth_token parameter is required".format(PACKET_API_TOKEN_ENV_VAR)) 222 module.fail_json(msg=_fail_msg) 223 224 auth_token = module.params.get('auth_token') 225 226 packet_conn = packet.Manager(auth_token=auth_token) 227 228 state = module.params.get('state') 229 230 if state in ['present', 'absent']: 231 if module.check_mode: 232 module.exit_json(changed=False) 233 234 try: 235 module.exit_json(**act_on_project(state, module, packet_conn)) 236 except Exception as e: 237 module.fail_json( 238 msg="failed to set project state {0}: {1}".format(state, to_native(e))) 239 else: 240 module.fail_json(msg="{0} is not a valid state for this module".format(state)) 241 242 243if __name__ == '__main__': 244 main() 245