├── .gitignore ├── README.md ├── __init__.py ├── foreman_architecture.py ├── foreman_compute_attribute.py ├── foreman_compute_profile.py ├── foreman_compute_resource.py ├── foreman_config_template.py ├── foreman_domain.py ├── foreman_environment.py ├── foreman_external_usergroup.py ├── foreman_filter.py ├── foreman_global_parameter.py ├── foreman_host.py ├── foreman_host_check.py ├── foreman_hostgroup.py ├── foreman_image.py ├── foreman_ldap.py ├── foreman_location.py ├── foreman_medium.py ├── foreman_operatingsystem.py ├── foreman_organization.py ├── foreman_os_default_template.py ├── foreman_ptable.py ├── foreman_realm.py ├── foreman_role.py ├── foreman_setting.py ├── foreman_smart_proxy.py ├── foreman_subnet.py ├── foreman_user.py ├── foreman_usergroup.py └── module_utils └── foreman_utils.py /.gitignore: -------------------------------------------------------------------------------- 1 | *.pyc 2 | .idea 3 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Ansible library Foreman 2 | ========== 3 | 4 | # Table of Contents 5 | - [Description](#description) 6 | - [Requirements](#requirements) 7 | - [Examples](#examples) 8 | - [License](#license) 9 | - [Author information](#author information) 10 | 11 | # Description 12 | Ansible library to configure [Foreman] and manage hosts. 13 | 14 | With the current implementation it's possible to create, update and delete the following Foreman Resources 15 | - Architectures 16 | - Compute Attributes 17 | - Compute Profiles 18 | - Compute Resources 19 | - Config Templates 20 | - Domains 21 | - Environments 22 | - Hosts 23 | - Hostgroups 24 | - Locations (needs Katello) 25 | - Medium 26 | - Operatingsystems 27 | - Operatingsystem default templates 28 | - Organizations (need s Katello) 29 | - Partition Tables 30 | - Roles 31 | - Smart Proxies 32 | - Subnets 33 | - Users 34 | 35 | # Requirements 36 | [python-foreman] >= 0.12.11 is required to be installed on the system where Ansible is started from. 37 | 38 | # Examples 39 | `ansible.cfg` has to contain path to modules and module_utils: 40 | ``` 41 | [default] 42 | library = ansible-module-foreman 43 | module_utils = ansible-module-foreman/module_utils 44 | ``` 45 | The following parameters are always required so the module knows how to connect to the Foreman [API v2]. 46 | They are replaced in the examples by three dots (...). 47 | 48 | ```yaml 49 | foreman_host: foreman.example.com 50 | foreman_port: 443 51 | foreman_user: admin 52 | foreman_pass: password 53 | ``` 54 | 55 | ## Architecture 56 | ```yaml 57 | - name: Ensure Architecture 58 | foreman_architecture: 59 | name: x86_64 60 | state: present 61 | foreman_host: foreman.example.com 62 | foreman_port: 443 63 | foreman_user: admin 64 | foreman_pass: password 65 | ``` 66 | 67 | ## Compute Profile 68 | ```yaml 69 | - name: Ensure Compute Profile 70 | foreman_compute_profile: 71 | name: 1-Small 72 | foreman_host: foreman.example.com 73 | foreman_port: 443 74 | foreman_user: admin 75 | foreman_pass: password 76 | ``` 77 | 78 | ## Compute Resource 79 | ```yaml 80 | - name: Ensure Compute Resource 81 | foreman_compute_resource: 82 | name: VMwareCluster01 83 | state: present 84 | url: vsphere.example.com 85 | provider: VMWare 86 | user: ansible 87 | password: secret 88 | server: vsphere.example.com 89 | ... 90 | ``` 91 | 92 | ## Compute Attribute 93 | This is an example to configure VMware vSphere attributes. 94 | ```yaml 95 | - name: Ensure Compute Attribute 96 | foreman_compute_attribute: 97 | compute_profile: 1-Small 98 | compute_resource: VMwareCluster1 99 | vm_attributes: 100 | cluster: Cluster1 101 | corespersocket: 1 102 | cpus: 1 103 | guest_id: otherGuest64 104 | hardware_version: vmx-10 105 | interfaces_attributes: 106 | '0': 107 | _delete: '' 108 | network: network-40 109 | type: VirtualVmxnet3 110 | '1': 111 | _delete: '' 112 | network: network-40 113 | type: VirtualVmxnet3 114 | new_interfaces: 115 | _delete: '' 116 | network: network-40 117 | type: VirtualVmxnet3 118 | memory_mb: 1024 119 | path: /Datacenters/DC01/vm/example 120 | scsi_controller_type: ParaVirtualSCSIController 121 | volumes_attributes: 122 | '0': 123 | _delete: '' 124 | datastore: DS01 125 | eager_zero: true 126 | name: Hard disk 127 | size_gb: 16 128 | thin: true 129 | new_volumes: 130 | _delete: '' 131 | datastore: DS01 132 | eager_zero: true 133 | name: Hard disk 134 | size_gb: 16 135 | thin: true 136 | ... 137 | ``` 138 | 139 | ## Config Template 140 | ### Deploy existing file 141 | ```yaml 142 | - name: Ensure Config Template 143 | foreman_config_template: 144 | name: CoreOS Cloud-config 145 | locked: false 146 | operatingsystems: 147 | - CoreOS 148 | template_file: files/coreos-cloud-config 149 | snippet: true 150 | state: present 151 | ... 152 | ``` 153 | ### Deploy content 154 | ```yaml 155 | - name: Ensure Config Template 156 | foreman_config_template: 157 | name: CoreOS Cloud-config 158 | locked: false 159 | operatingsystems: 160 | - CoreOS 161 | template: "Some content" 162 | snippet: true 163 | state: present 164 | ... 165 | ``` 166 | 167 | ## Domain 168 | ```yaml 169 | - name: Ensure Domain 170 | foreman_domain: 171 | name: example.com 172 | state: present 173 | ... 174 | ``` 175 | 176 | ## Environments 177 | ```yaml 178 | - name: Ensure Environment 179 | foreman_environment: 180 | name: Production 181 | state: present 182 | ... 183 | ``` 184 | ## Host 185 | ### Provision using installation from medium 186 | ```yaml 187 | - name: Ensure Host 188 | foreman_host: 189 | name: ansible-host-01 190 | state: running 191 | architecture: x86_64 192 | domain: example.com 193 | environment: production 194 | medium: CoreOS 195 | provision_method: build 196 | root_pass: topsecret 197 | ``` 198 | 199 | ### Provision by clone from image 200 | ```yaml 201 | - name: Ensure Host 202 | foreman_host: 203 | name: ansible-host-03 204 | state: running 205 | compute_resource: VMwareCluster01 206 | hostgroup: Hostgroup01 207 | image: CoreOS Image 208 | provision_method: image 209 | ... 210 | ``` 211 | ### Provision using a hostgroup 212 | ```yaml 213 | - name: Ensure Host 214 | foreman_host: 215 | name: ansible-host-02 216 | state: running 217 | compute_resource: VMwareCluster01 218 | hostgroup: Hostgroup01 219 | provision_method: build 220 | ... 221 | ``` 222 | ### Delete host 223 | To delete a host Foreman must know the FQDN. Use one of the following methods: 224 | ```yaml 225 | - name: Ensure absent host 226 | foreman_host: 227 | name: ansible-host-01 228 | domain: example.com 229 | state: absent 230 | ... 231 | ``` 232 | or 233 | ```yaml 234 | - name: Ensure absent host 235 | foreman_host: 236 | name: ansible-host-01.example.com 237 | state: absent 238 | ... 239 | ``` 240 | ### Host with network interfaces 241 | ```yaml 242 | - name: Ensure Host 243 | foreman_host: 244 | name: ansible-host-04 245 | state: present 246 | . 247 | . 248 | interfaces: 249 | - ip: 10.11.12.123 250 | mac: 00:21:f6:16:e4:2e 251 | subnet: "Dev Network" 252 | - ip: 10.11.12.124 253 | mac: 00:21:f6:42:52:91 254 | subnet: "Dev Network" 255 | ... 256 | ``` 257 | ## Hostgroup 258 | ```yaml 259 | - name: Ensure Hostgroup 260 | foreman_hostgroup: 261 | name: Hostgroup01 262 | architecture: x86_64 263 | domain: example.com 264 | environment: production 265 | medium: CoreOS mirror 266 | operatingsystem: CoreOS 267 | partition_table: CoreOS Partition Table 268 | subnet: example.com 269 | state: present 270 | ... 271 | ``` 272 | 273 | ## Location 274 | ``` 275 | - name: Ensure Location 276 | foreman_location: 277 | name: Location01 278 | state: present 279 | ... 280 | ``` 281 | 282 | ## Medium 283 | ```yaml 284 | - name: Ensure Medium 285 | foreman_medium: 286 | name: CoreOS 287 | path: http://$release.release.core-os.net 288 | os_family: CoreOS 289 | state: present 290 | ... 291 | ``` 292 | 293 | ## Operatingsystem 294 | ```yaml 295 | - name: Ensure Operatingsystem 296 | foreman_operatingsystem: 297 | name: CoreOS 298 | major: 633 299 | minor: 0.0 300 | architectures: 301 | - x86_64 302 | media: 303 | - CoreOS mirror 304 | ptables: 305 | - CoreOS default fake 306 | state: present 307 | ... 308 | ``` 309 | 310 | ## Operatingsystem default template 311 | ```yaml 312 | - name: Ensure Operatingsystem default template 313 | foreman_os_default_template: 314 | operatingsystem: CoreOS 315 | config_template: CoreOS PXELinux 316 | template_kind: PXELinux 317 | state: present 318 | ... 319 | ``` 320 | 321 | ## Organization 322 | Works only if Katello is used 323 | ```yaml 324 | - name: Ensure Organization 325 | foreman_organization: 326 | name: MyOrganization 327 | state: present 328 | ... 329 | ``` 330 | 331 | ## Partition Table 332 | ```yaml 333 | - name: Ensure partition table 334 | foreman_ptable: 335 | name: MyPartitionTable 336 | layout: 'some layout' 337 | os_family: CoreOS 338 | state: present 339 | ... 340 | ``` 341 | ## Role 342 | ```yaml 343 | - name: Ensure Role 344 | foreman_role: 345 | name: MyRole 346 | state: present 347 | ``` 348 | 349 | ## Smart Proxy 350 | ```yaml 351 | - name: Ensure Smart Proxy 352 | foreman_smart_proxy: 353 | name: SmartProxy01 354 | url: http://localhost:8443 355 | state: present 356 | ... 357 | ``` 358 | 359 | ## User 360 | ```yaml 361 | - name: Ensure User 362 | foreman_user: 363 | login: MyUser 364 | admin: false 365 | auth: 'Internal' 366 | firstname: Testing 367 | lastname: User 368 | mail: testing.user@example.com 369 | password: topsecret 370 | roles: 371 | - Manager 372 | - Viewer 373 | state: present 374 | ``` 375 | 376 | # License 377 | 378 | Copyright 2015 Thomas Krahn 379 | 380 | Licensed under the Apache License, Version 2.0 (the "License"); 381 | you may not use this file except in compliance with the License. 382 | You may obtain a copy of the License at 383 | 384 | http://www.apache.org/licenses/LICENSE-2.0 385 | 386 | Unless required by applicable law or agreed to in writing, software 387 | distributed under the License is distributed on an "AS IS" BASIS, 388 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 389 | See the License for the specific language governing permissions and 390 | limitations under the License. 391 | 392 | # Author 393 | 394 | [Thomas Krahn] 395 | 396 | [Foreman]: www.theforeman.org 397 | [API v2]: www.theforeman.org/api_v2.html 398 | [python-foreman]: https://github.com/Nosmoht/python-foreman 399 | [Thomas Krahn]: mailto:ntbc@gmx.net 400 | -------------------------------------------------------------------------------- /__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Nosmoht/ansible-module-foreman/d5509cc74025c842fb1c63aae4e1d36c31ce026d/__init__.py -------------------------------------------------------------------------------- /foreman_architecture.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # -*- coding: utf-8 -*- 3 | # 4 | # Ansible module to manage Foreman architecture resources. 5 | # 6 | # This module is free software: you can redistribute it and/or modify 7 | # it under the terms of the GNU General Public License as published by 8 | # the Free Software Foundation, either version 3 of the License, or 9 | # (at your option) any later version. 10 | # 11 | # This software is distributed in the hope that it will be useful, 12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | # GNU General Public License for more details. 15 | # 16 | # You should have received a copy of the GNU General Public License 17 | # along with this software. If not, see . 18 | 19 | DOCUMENTATION = ''' 20 | --- 21 | module: foreman_architecture 22 | short_description: Manage Foreman Architectures using Foreman API v2 23 | description: 24 | - Create and delete Foreman Architectures using Foreman API v2 25 | options: 26 | name: 27 | description: Name of architecture 28 | required: true 29 | aliases: [] 30 | state: 31 | description: State of architecture 32 | required: false 33 | default: present 34 | choices: ["present", "absent"] 35 | foreman_host: 36 | description: Hostname or IP address of Foreman system 37 | required: false 38 | default: 127.0.0.1 39 | foreman_port: 40 | description: Port of Foreman API 41 | required: false 42 | default: 443 43 | foreman_user: 44 | description: Username to be used to authenticate on Foreman 45 | required: true 46 | foreman_pass: 47 | description: Password to be used to authenticate user on Foreman 48 | required: true 49 | foreman_ssl: 50 | description: Enable SSL when connecting to Foreman API 51 | required: false 52 | default: true 53 | requires: 54 | - python-foreman > 0.14.0 55 | author: "Thomas Krahn (@nosmoht)" 56 | ''' 57 | 58 | EXAMPLES = ''' 59 | - name: Ensure ARM Architecture is present 60 | foreman_architecture: 61 | name: ARM 62 | state: present 63 | foreman_user: admin 64 | foreman_pass: secret 65 | foreman_host: foreman.example.com 66 | foreman_port: 443 67 | ''' 68 | 69 | try: 70 | from foreman.foreman import * 71 | except ImportError: 72 | foremanclient_found = False 73 | else: 74 | foremanclient_found = True 75 | 76 | 77 | def ensure(module, theforeman): 78 | name = module.params['name'] 79 | state = module.params['state'] 80 | 81 | data = dict(name=name) 82 | 83 | try: 84 | arch = theforeman.search_architecture(data=data) 85 | except ForemanError as e: 86 | module.fail_json(msg='Could not get architecture: {0}'.format(e.message)) 87 | 88 | if not arch and state == 'present': 89 | try: 90 | arch = theforeman.create_architecture(data) 91 | except ForemanError as e: 92 | module.fail_json(msg='Could not create architecture: {0}'.format(e.message)) 93 | return True, arch 94 | 95 | if arch and state == 'absent': 96 | try: 97 | arch = theforeman.delete_architecture(id=arch.get('id')) 98 | except ForemanError as e: 99 | module.fail_json(msg='Could not delete architecture: {0}'.format(e.message)) 100 | return True, arch 101 | 102 | return False, arch 103 | 104 | 105 | def main(): 106 | module = AnsibleModule( 107 | argument_spec=dict( 108 | name=dict(type='str', required=True), 109 | state=dict(type='str', default='present', choices=['present', 'absent']), 110 | foreman_host=dict(type='str', default='127.0.0.1'), 111 | foreman_port=dict(type='str', default='443'), 112 | foreman_user=dict(type='str', required=True), 113 | foreman_pass=dict(type='str', required=True, no_log=True), 114 | foreman_ssl=dict(type='bool', default=True) 115 | ), 116 | ) 117 | 118 | if not foremanclient_found: 119 | module.fail_json(msg='python-foreman module is required. See https://github.com/Nosmoht/python-foreman.') 120 | 121 | foreman_host = module.params['foreman_host'] 122 | foreman_port = module.params['foreman_port'] 123 | foreman_user = module.params['foreman_user'] 124 | foreman_pass = module.params['foreman_pass'] 125 | foreman_ssl = module.params['foreman_ssl'] 126 | 127 | theforeman = Foreman(hostname=foreman_host, 128 | port=foreman_port, 129 | username=foreman_user, 130 | password=foreman_pass, 131 | ssl=foreman_ssl) 132 | 133 | changed, arch = ensure(module, theforeman) 134 | module.exit_json(changed=changed, architecture=arch) 135 | 136 | 137 | from ansible.module_utils.basic import * 138 | 139 | if __name__ == '__main__': 140 | main() 141 | -------------------------------------------------------------------------------- /foreman_compute_attribute.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # -*- coding: utf-8 -*- 3 | # 4 | # Ansible module to manage Foreman compute attribute resources. 5 | # 6 | # This module is free software: you can redistribute it and/or modify 7 | # it under the terms of the GNU General Public License as published by 8 | # the Free Software Foundation, either version 3 of the License, or 9 | # (at your option) any later version. 10 | # 11 | # This software is distributed in the hope that it will be useful, 12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | # GNU General Public License for more details. 15 | # 16 | # You should have received a copy of the GNU General Public License 17 | # along with this software. If not, see . 18 | 19 | DOCUMENTATION = ''' 20 | --- 21 | module: foreman_compute_attribute 22 | short_description: Manage Foreman Compute Attributes using Foreman API v2 23 | description: 24 | - Create and update Foreman Compute Attributes using Foreman API v2 25 | options: 26 | compute_resource: 27 | description: Name of compute resource 28 | required: true 29 | compute_profile: 30 | description: Name of compute profile 31 | required: true 32 | vm_attributes: 33 | description: Hash containing the data of vm_attrs 34 | required: true 35 | foreman_host: 36 | description: Hostname or IP address of Foreman system 37 | required: false 38 | default: 127.0.0.1 39 | foreman_port: 40 | description: Port of Foreman API 41 | required: false 42 | default: 443 43 | foreman_user: 44 | description: Username to be used to authenticate on Foreman 45 | required: true 46 | foreman_pass: 47 | description: Password to be used to authenticate user on Foreman 48 | required: true 49 | foreman_ssl: 50 | description: Enable SSL when connecting to Foreman API 51 | required: false 52 | default: true 53 | notes: 54 | - Requires the python-foreman package to be installed. See https://github.com/Nosmoht/python-foreman. 55 | version_added: "2.0" 56 | author: "Thomas Krahn (@nosmoht)" 57 | ''' 58 | 59 | try: 60 | from foreman.foreman import * 61 | 62 | foremanclient_found = True 63 | except ImportError: 64 | foremanclient_found = False 65 | 66 | 67 | def ensure(module): 68 | compute_profile_name = module.params['compute_profile'] 69 | compute_resource_name = module.params['compute_resource'] 70 | vm_attributes = module.params['vm_attributes'] 71 | 72 | foreman_host = module.params['foreman_host'] 73 | foreman_port = module.params['foreman_port'] 74 | foreman_user = module.params['foreman_user'] 75 | foreman_pass = module.params['foreman_pass'] 76 | foreman_ssl = module.params['foreman_ssl'] 77 | 78 | theforeman = Foreman(hostname=foreman_host, 79 | port=foreman_port, 80 | username=foreman_user, 81 | password=foreman_pass, 82 | ssl=foreman_ssl) 83 | 84 | try: 85 | compute_resource = theforeman.search_compute_resource(data={'name': compute_resource_name}) 86 | if not compute_resource: 87 | module.fail_json(msg='Compute resource not found: {0}'.format(compute_resource_name)) 88 | except ForemanError as e: 89 | module.fail_json(msg='Could not get compute resource: {0}'.format(compute_resource_name)) 90 | 91 | try: 92 | compute_profile = theforeman.search_compute_profile(data={'name': compute_profile_name}) 93 | if not compute_profile: 94 | module.fail_json(msg='Compute profile {0} not found on {1}'.format(compute_profile_name, 95 | compute_resource_name)) 96 | except ForemanError as e: 97 | module.fail_json(msg='Could not get compute profile {0} on {1}'.format(compute_profile_name, 98 | compute_resource_name)) 99 | 100 | compute_attributes = theforeman.get_compute_attribute(compute_resource_id=compute_resource.get('id'), 101 | compute_profile_id=compute_profile.get('id')) 102 | 103 | if compute_attributes: 104 | try: 105 | # Python 2 106 | compute_attribute = compute_attributes[0] 107 | except TypeError: 108 | # Python 3 109 | compute_attribute = next(compute_attributes) 110 | else: 111 | compute_attribute = None 112 | 113 | if not compute_attribute: 114 | try: 115 | compute_attribute = theforeman.create_compute_attribute(compute_resource_id=compute_resource.get('id'), 116 | compute_profile_id=compute_profile.get('id'), 117 | data={'vm_attrs': vm_attributes}) 118 | return True, compute_attribute 119 | except ForemanError as e: 120 | module.fail_json(msg='Could not create compute attribute: {0}'.format(e.message)) 121 | 122 | if not all(compute_attribute['vm_attrs'].get(key, vm_attributes.get(key)) == vm_attributes.get(key) for key in 123 | vm_attributes) != 0: 124 | try: 125 | compute_attribute = theforeman.update_compute_attribute(id=compute_attribute.get('id'), 126 | data=vm_attributes) 127 | return True, compute_attribute 128 | except ForemanError as e: 129 | module.fail_json(msg='Could not update compute attribute: {0}'.format(e.message)) 130 | 131 | return False, compute_attribute 132 | 133 | 134 | def main(): 135 | module = AnsibleModule( 136 | argument_spec=dict( 137 | compute_profile=dict(type='str', required=True), 138 | compute_resource=dict(type='str', required=True), 139 | vm_attributes=dict(type='dict', required=False), 140 | foreman_host=dict(type='str', Default='127.0.0.1'), 141 | foreman_port=dict(type='str', Default='443'), 142 | foreman_user=dict(type='str', required=True), 143 | foreman_pass=dict(type='str', required=True, no_log=True), 144 | foreman_ssl=dict(type='bool', required=False, default=True) 145 | ), 146 | ) 147 | 148 | if not foremanclient_found: 149 | module.fail_json(msg='python-foreman is required. See https://github.com/Nosmoht/python-foreman.') 150 | 151 | changed, compute_attribute = ensure(module) 152 | module.exit_json(changed=changed, compute_attribute=compute_attribute) 153 | 154 | 155 | from ansible.module_utils.basic import * 156 | 157 | if __name__ == '__main__': 158 | main() 159 | -------------------------------------------------------------------------------- /foreman_compute_profile.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # -*- coding: utf-8 -*- 3 | # 4 | # Ansible module to manage Foreman compute profile resources. 5 | # 6 | # This module is free software: you can redistribute it and/or modify 7 | # it under the terms of the GNU General Public License as published by 8 | # the Free Software Foundation, either version 3 of the License, or 9 | # (at your option) any later version. 10 | # 11 | # This software is distributed in the hope that it will be useful, 12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | # GNU General Public License for more details. 15 | # 16 | # You should have received a copy of the GNU General Public License 17 | # along with this software. If not, see . 18 | 19 | DOCUMENTATION = ''' 20 | --- 21 | module: foreman_compute_profile 22 | short_description: Manage Foreman Compute Profiles using Foreman API v2 23 | description: 24 | - Create and delete Foreman Compute Profiles using Foreman API v2 25 | options: 26 | name: 27 | description: Name of Compute Profile 28 | required: true 29 | state: 30 | description: State of Compute Profile 31 | required: false 32 | default: present 33 | choices: ["present", "absent"] 34 | foreman_host: 35 | description: Hostname or IP address of Foreman 36 | required: false 37 | default: 127.0.0.1 38 | foreman_port: 39 | description: Port of Foreman API 40 | required: false 41 | default: 443 42 | foreman_user: 43 | description: Username to be used to authenticate on Foreman 44 | required: true 45 | foreman_pass: 46 | description: Password to be used to authenticate user on Foreman 47 | required: true 48 | foreman_ssl: 49 | description: Enable SSL when connecting to Foreman API 50 | required: false 51 | default: true 52 | notes: 53 | - Requires the python-foreman package to be installed. See https://github.com/Nosmoht/python-foreman. 54 | version_added: "2.0" 55 | author: "Thomas Krahn (@nosmoht)" 56 | ''' 57 | 58 | EXAMPLES = ''' 59 | - name: Ensure Extra Large Compute Profile is present 60 | foreman_compute_profile: 61 | name: 4-Extra-Large 62 | state: present 63 | foreman_user: admin 64 | foreman_pass: secret 65 | ''' 66 | 67 | try: 68 | from foreman.foreman import * 69 | except ImportError: 70 | foremanclient_found = False 71 | else: 72 | foremanclient_found = True 73 | 74 | 75 | def ensure(module): 76 | name = module.params['name'] 77 | state = module.params['state'] 78 | 79 | foreman_host = module.params['foreman_host'] 80 | foreman_port = module.params['foreman_port'] 81 | foreman_user = module.params['foreman_user'] 82 | foreman_pass = module.params['foreman_pass'] 83 | foreman_ssl = module.params['foreman_ssl'] 84 | 85 | theforeman = Foreman(hostname=foreman_host, 86 | port=foreman_port, 87 | username=foreman_user, 88 | password=foreman_pass, 89 | ssl=foreman_ssl) 90 | 91 | data = dict(name=name) 92 | 93 | try: 94 | compute_profile = theforeman.search_compute_profile(data=data) 95 | except ForemanError as e: 96 | module.fail_json(msg='Could not get compute profile: {0}'.format(e.message)) 97 | 98 | if state == 'absent': 99 | if compute_profile: 100 | try: 101 | compute_profile = theforeman.delete_compute_profile(id=compute_profile.get('id')) 102 | except ForemanError as e: 103 | module.fail_json(msg='Could not delete compute profile: {0}'.format(e.message)) 104 | return True, compute_profile 105 | return False, compute_profile 106 | 107 | if state == 'present': 108 | if not compute_profile: 109 | try: 110 | compute_profile = theforeman.create_compute_profile(data=data) 111 | except ForemanError as e: 112 | module.fail_json(msg='Could not create compute profile: {0}'.format(e.message)) 113 | return True, compute_profile 114 | 115 | return False, compute_profile 116 | 117 | 118 | def main(): 119 | module = AnsibleModule( 120 | argument_spec=dict( 121 | name=dict(type='str', required=True), 122 | state=dict(type='str', default='present', choices=['present', 'absent']), 123 | foreman_host=dict(type='str', default='127.0.0.1'), 124 | foreman_port=dict(type='str', default='443'), 125 | foreman_user=dict(type='str', required=True), 126 | foreman_pass=dict(type='str', required=True, no_log=True), 127 | foreman_ssl=dict(type='bool', default=True) 128 | ), 129 | ) 130 | 131 | if not foremanclient_found: 132 | module.fail_json(msg='python-foreman module is required. See https://github.com/Nosmoht/python-foreman.') 133 | 134 | changed, compute_profile = ensure(module) 135 | module.exit_json(changed=changed, compute_profile=compute_profile) 136 | 137 | 138 | from ansible.module_utils.basic import * 139 | 140 | if __name__ == '__main__': 141 | main() 142 | -------------------------------------------------------------------------------- /foreman_compute_resource.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # -*- coding: utf-8 -*- 3 | # 4 | # Ansible module to manage Foreman compute resource resources. 5 | # 6 | # This module is free software: you can redistribute it and/or modify 7 | # it under the terms of the GNU General Public License as published by 8 | # the Free Software Foundation, either version 3 of the License, or 9 | # (at your option) any later version. 10 | # 11 | # This software is distributed in the hope that it will be useful, 12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | # GNU General Public License for more details. 15 | # 16 | # You should have received a copy of the GNU General Public License 17 | # along with this software. If not, see . 18 | 19 | DOCUMENTATION = ''' 20 | --- 21 | module: foreman_compute_resource 22 | short_description: Manage Foreman Compute resources using Foreman API v2 23 | description: 24 | - Create and delete Foreman Compute Resources using Foreman API v2 25 | options: 26 | name: 27 | description: Compute Resource name 28 | required: true 29 | datacenter: Name of Datacenter (only for Vmware) 30 | required: false 31 | default: None 32 | description: Description of compute resource 33 | required: false 34 | default: None 35 | locations: List of locations the compute resource should be assigned to 36 | required: false 37 | default: None 38 | organizations: List of organizations the compute resource should be assigned to 39 | required: false 40 | default: None 41 | password: 42 | description: Password for Ovirt, EC2, Vmware, Openstack. Secret key for EC2 43 | required: false 44 | default: None 45 | provider: 46 | description: Providers name (e.g. Ovirt, EC2, Vmware, Openstack, EC2, Google) 47 | required: false 48 | default: None 49 | server: 50 | description: Hostname of Vmware vSphere system 51 | required: false 52 | default: None 53 | state: 54 | description: Compute Resource state 55 | required: false 56 | default: present 57 | choices: ["present", "absent"] 58 | tenant: 59 | description: Tenant name for Openstack 60 | required: false 61 | default: None 62 | domain: 63 | description: Domain name for Openstack (required for Keystone v3 API) 64 | required: false 65 | default: Default 66 | url: 67 | description: URL for Libvirt, Ovirt, and Openstack 68 | required: false 69 | default: None 70 | user: 71 | description: Username for Ovirt, EC2, Vmware, Openstack. Access Key for EC2. 72 | required: false 73 | default: None 74 | foreman_host: 75 | description: Hostname or IP address of Foreman system 76 | required: false 77 | default: 127.0.0.1 78 | foreman_port: 79 | description: Port of Foreman API 80 | required: false 81 | default: 443 82 | foreman_user: 83 | description: Username to be used to authenticate on Foreman 84 | required: true 85 | foreman_pass: 86 | description: Password to be used to authenticate user on Foreman 87 | required: true 88 | foreman_ssl: 89 | description: Enable SSL when connecting to Foreman API 90 | required: false 91 | default: true 92 | notes: 93 | - Requires the python-foreman package to be installed. See https://github.com/Nosmoht/python-foreman. 94 | version_added: "2.0" 95 | author: "Thomas Krahn (@nosmoht)" 96 | ''' 97 | 98 | EXAMPLES = ''' 99 | - name: Ensure Vmware compute resource 100 | foreman_compute_resource: 101 | name: VMware 102 | datacenter: dc01 103 | locations: 104 | - Nevada 105 | organizations: 106 | - DevOps 107 | provider: VMware 108 | server: vsphere.example.com 109 | url: vsphere.example.com 110 | user: domain\admin 111 | password: secret 112 | state: present 113 | foreman_user: admin 114 | foreman_pass: secret 115 | - name: Ensure Openstack compute resource 116 | foreman_compute_resource: 117 | name: Openstack 118 | provider: OpenStack 119 | tenant: ExampleTenant 120 | url: https://compute01.example.com:5000/v2.0/tokens 121 | user: admin 122 | ''' 123 | 124 | try: 125 | from foreman.foreman import * 126 | except ImportError: 127 | foremanclient_found = False 128 | else: 129 | foremanclient_found = True 130 | 131 | 132 | def get_provider_params(provider): 133 | provider_name = provider.lower() 134 | 135 | if provider_name == 'docker': 136 | return ['password', 'url', 'user'] 137 | elif provider_name == 'ec2': 138 | return ['access_key', 'password', 'region', 'url', 'user'] 139 | elif provider_name == 'google': 140 | return ['email', 'key_path', 'project', 'url', 'zone'] 141 | elif provider_name == 'libvirt': 142 | return ['display_type', 'url'] 143 | elif provider_name == 'ovirt': 144 | return ['url', 'user', 'password'] 145 | elif provider_name == 'openstack': 146 | return ['url', 'user', 'password', 'tenant', 'domain'] 147 | elif provider_name == 'vmware': 148 | return ['datacenter', 'user', 'password', 'server'] 149 | else: 150 | return [] 151 | 152 | def get_organization_ids(module, theforeman, organizations): 153 | result = [] 154 | for i in range(0, len(organizations)): 155 | try: 156 | organization = theforeman.search_organization(data={'name': organizations[i]}) 157 | if not organization: 158 | module.fail_json('Could not find Organization {0}'.format(organizations[i])) 159 | result.append(organization.get('id')) 160 | except ForemanError as e: 161 | module.fail_json('Could not get Organizations: {0}'.format(e.message)) 162 | return result 163 | 164 | 165 | def get_location_ids(module, theforeman, locations): 166 | result = [] 167 | for i in range(0, len(locations)): 168 | try: 169 | location = theforeman.search_location(data={'name':locations[i]}) 170 | if not location: 171 | module.fail_json('Could not find Location {0}'.format(locations[i])) 172 | result.append(location.get('id')) 173 | except ForemanError as e: 174 | module.fail_json('Could not get Locations: {0}'.format(e.message)) 175 | return result 176 | 177 | 178 | def ensure(module): 179 | name = module.params['name'] 180 | state = module.params['state'] 181 | provider = module.params['provider'] 182 | description = module.params['description'] 183 | locations = module.params['locations'] 184 | organizations = module.params['organizations'] 185 | 186 | foreman_host = module.params['foreman_host'] 187 | foreman_port = module.params['foreman_port'] 188 | foreman_user = module.params['foreman_user'] 189 | foreman_pass = module.params['foreman_pass'] 190 | foreman_ssl = module.params['foreman_ssl'] 191 | 192 | theforeman = Foreman(hostname=foreman_host, 193 | port=foreman_port, 194 | username=foreman_user, 195 | password=foreman_pass, 196 | ssl=foreman_ssl) 197 | 198 | data = dict(name=name) 199 | 200 | 201 | try: 202 | compute_resource = theforeman.search_compute_resource(data=data) 203 | except ForemanError as e: 204 | module.fail_json(msg='Could not get compute resource: {0}'.format(e.message)) 205 | 206 | if organizations: 207 | data['organization_ids'] = get_organization_ids(module, theforeman, organizations) 208 | 209 | if locations: 210 | data['location_ids'] = get_location_ids(module, theforeman, locations) 211 | 212 | if state == 'absent': 213 | if compute_resource: 214 | try: 215 | compute_resource = theforeman.delete_compute_resource(id=compute_resource.get('id')) 216 | except ForemanError as e: 217 | module.fail_json(msg='Could not delete compute resource: {0}'.format(e.message)) 218 | return True, compute_resource 219 | 220 | data['provider'] = provider 221 | params = get_provider_params(provider=provider) + ['description'] 222 | for key in params: 223 | data[key] = module.params[key] 224 | 225 | if state == 'present': 226 | if not compute_resource: 227 | try: 228 | compute_resource = theforeman.create_compute_resource(data=data) 229 | except ForemanError as e: 230 | module.fail_json(msg='Could not create compute resource: {0}'.format(e.message)) 231 | return True, compute_resource 232 | 233 | # Foreman's API doesn't return the password we can't tell if we need to 234 | # change it 235 | data['password'] = None 236 | if not all(data.get(key, None) == compute_resource.get(key, None) for key in params): 237 | try: 238 | compute_resource = theforeman.update_compute_resource(id=compute_resource.get('id'), data=data) 239 | except ForemanError as e: 240 | module.fail_json(msg='Could not update compute resource: {0}'.format(e.message)) 241 | return True, compute_resource 242 | 243 | return False, compute_resource 244 | 245 | 246 | def main(): 247 | module = AnsibleModule( 248 | argument_spec=dict( 249 | name=dict(type='str', required=True), 250 | access_key=dict(type='str', requireD=False), 251 | datacenter=dict(type='str', required=False), 252 | description=dict(type='str', required=False), 253 | display_type=dict(type='str', required=False), 254 | email=dict(type='str', required=False), 255 | key_path=dict(type='str', required=False), 256 | password=dict(type='str', required=False, no_log=True), 257 | provider=dict(type='str', required=False), 258 | region=dict(type='str', required=False), 259 | server=dict(type='str', required=False), 260 | url=dict(type='str', required=False), 261 | user=dict(type='str', required=False), 262 | state=dict(type='str', default='present', choices=['present', 'absent']), 263 | tenat=dict(type='str', required=False), 264 | domain=dict(type='str', required=False), 265 | locations=dict(type='list', required=False), 266 | organizations=dict(type='list', required=False), 267 | foreman_host=dict(type='str', default='127.0.0.1'), 268 | foreman_port=dict(type='str', default='443'), 269 | foreman_user=dict(type='str', required=True), 270 | foreman_pass=dict(type='str', required=True, no_log=True), 271 | foreman_ssl=dict(type='bool', default=True) 272 | ), 273 | ) 274 | 275 | if not foremanclient_found: 276 | module.fail_json(msg='python-foreman module is required. See https://github.com/Nosmoht/python-foreman.') 277 | 278 | changed, compute_resource = ensure(module) 279 | module.exit_json(changed=changed, compute_resource=compute_resource) 280 | 281 | 282 | from ansible.module_utils.basic import * 283 | 284 | if __name__ == '__main__': 285 | main() 286 | -------------------------------------------------------------------------------- /foreman_config_template.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # -*- coding: utf-8 -*- 3 | # 4 | # Ansible module to manage Foreman config template resources. 5 | # 6 | # This module is free software: you can redistribute it and/or modify 7 | # it under the terms of the GNU General Public License as published by 8 | # the Free Software Foundation, either version 3 of the License, or 9 | # (at your option) any later version. 10 | # 11 | # This software is distributed in the hope that it will be useful, 12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | # GNU General Public License for more details. 15 | # 16 | # You should have received a copy of the GNU General Public License 17 | # along with this software. If not, see . 18 | 19 | DOCUMENTATION = ''' 20 | --- 21 | module: foreman_config_template 22 | short_description: 23 | - Manage Foreman Provision Templates using Foreman API v2. 24 | description: 25 | - Create, update and and delete Foreman provision templates using Foreman API v2 26 | options: 27 | audit_comment: 28 | description: 29 | - Audit comment 30 | required: false 31 | default: None 32 | name: 33 | description: Provision template name 34 | required: true 35 | default: None 36 | locked: 37 | description: Whether or not the template is locked for editing 38 | required: false 39 | default: None 40 | operatingsystems: 41 | description: List of Operatingsystem names the template is assigned to 42 | required: false 43 | default: None 44 | template: 45 | description: RAW template content 46 | required: false 47 | default: None 48 | template_file: 49 | description: Path and filename to load the template from 50 | required: false 51 | default: None 52 | template_kind_name: 53 | description: Template Kind name 54 | required: false 55 | default: None 56 | snippet: 57 | description: Define if template is a snippet or not 58 | required: false 59 | default: None 60 | organizations: 61 | description: 62 | - List of organization the medium should be assigned to 63 | required: false 64 | locations: 65 | description: 66 | - List of locations the medium should be assigned to 67 | required: false 68 | state: 69 | description: Provision template state 70 | required: false 71 | default: 'present' 72 | choices: ['present', 'absent'] 73 | foreman_host: 74 | description: Hostname or IP address of Foreman system 75 | required: false 76 | default: 127.0.0.1 77 | foreman_port: 78 | description: Port of Foreman API 79 | required: false 80 | default: 443 81 | foreman_user: 82 | description: Username to be used to authenticate on Foreman 83 | required: true 84 | foreman_pass: 85 | description: Password to be used to authenticate user on Foreman 86 | required: true 87 | foreman_ssl: 88 | description: Enable SSL when connecting to Foreman API 89 | required: false 90 | default: true 91 | notes: 92 | - Requires the python-foreman package to be installed. See https://github.com/Nosmoht/python-foreman. 93 | version_added: "2.0" 94 | author: "Thomas Krahn (@nosmoht)" 95 | ''' 96 | 97 | EXAMPLES = ''' 98 | - name: Ensure Config Template 99 | foreman_config_template: 100 | name: CoreOS Cloud-config 101 | locked: false 102 | operatingsystems: 103 | - CoreOS 104 | template_file: /tmp/coreos-cloud-config 105 | template_kind_name: provision 106 | snippet: true 107 | organizations: 108 | - Development 109 | - DevOps 110 | locations: 111 | - Prague 112 | state: present 113 | foreman_host: 127.0.0.1 114 | foreman_port: 443 115 | foreman_user: admin 116 | foreman_pass: secret 117 | ''' 118 | 119 | try: 120 | from foreman.foreman import * 121 | 122 | foremanclient_found = True 123 | except ImportError: 124 | foremanclient_found = False 125 | 126 | try: 127 | from ansible.module_utils.foreman_utils import * 128 | 129 | has_import_error = False 130 | except ImportError as e: 131 | has_import_error = True 132 | import_error_msg = str(e) 133 | 134 | 135 | def templates_equal(data, config_template): 136 | comparable_keys = set(data.keys()).intersection(set( 137 | ['locked', 'snippet', 'template', 'audit_comment', 'template_kind_id'])) 138 | if not all(data.get(key, None) == config_template.get(key, None) for key in comparable_keys): 139 | return False 140 | if not operatingsystems_equal(data, config_template): 141 | return False 142 | if not organizations_equal(data, config_template): 143 | return False 144 | if not locations_equal(data, config_template): 145 | return False 146 | return True 147 | 148 | 149 | def get_resources(resource_type, resource_func, resource_specs, search_field='name'): 150 | result = list() 151 | if not resource_specs: 152 | return result 153 | for item in resource_specs: 154 | search_data = dict() 155 | if isinstance(item, dict): 156 | for key in item: 157 | search_data[key] = item[key] 158 | else: 159 | search_data[search_field] = item 160 | try: 161 | resource = resource_func(data=search_data) 162 | if not resource: 163 | module.fail_json( 164 | msg='Could not find resource type {resource_type} specified as {name}'.format( 165 | resource_type=resource_type, 166 | name=item)) 167 | result.append(resource) 168 | except ForemanError as e: 169 | module.fail_json(msg='Could not search resource type {resource_type} specified as {name}: {error}'.format( 170 | resource_type=resource_type, name=item, error=e.message)) 171 | return result 172 | 173 | 174 | def ensure(): 175 | audit_comment = module.params['audit_comment'] 176 | locked = module.params['locked'] 177 | name = module.params['name'] 178 | operatingsystems = module.params['operatingsystems'] 179 | state = module.params['state'] 180 | snippet = module.params['snippet'] 181 | template = module.params['template'] 182 | template_file = module.params['template_file'] 183 | template_kind_name = module.params['template_kind_name'] 184 | organizations = module.params['organizations'] 185 | locations = module.params['locations'] 186 | 187 | theforeman = init_foreman_client(module) 188 | 189 | data = dict(name=name) 190 | 191 | try: 192 | config_template = theforeman.search_config_template(data=data) 193 | if config_template: 194 | config_template = theforeman.get_config_template(id=config_template.get('id')) 195 | except ForemanError as e: 196 | module.fail_json(msg='Could not get config template: {0}'.format(e.message)) 197 | 198 | if state == 'absent': 199 | if config_template: 200 | try: 201 | config_template = theforeman.delete_config_template(id=config_template.get('id')) 202 | return True, config_template 203 | except ForemanError as e: 204 | module.fail_json(msg='Could not delete config template: {0}'.format(e.message)) 205 | 206 | if state == 'present': 207 | if template and template_file: 208 | module.fail_json(msg='Only one of either template or template_file must be defined') 209 | if template: 210 | data['template'] = template 211 | elif template_file: 212 | try: 213 | with open(template_file) as f: 214 | data['template'] = f.read() 215 | except IOError as e: 216 | module.fail_json(msg='Could not open file {0}: {1}'.format(template_file, e.message)) 217 | 218 | data['audit_comment'] = audit_comment 219 | if locked: 220 | data['locked'] = locked 221 | if snippet: 222 | data['snippet'] = snippet 223 | 224 | if organizations is not None: 225 | data['organization_ids'] = get_organization_ids(module, theforeman, organizations) 226 | if locations is not None: 227 | data['location_ids'] = get_location_ids(module, theforeman, locations) 228 | if operatingsystems is not None: 229 | data['operatingsystem_ids'] = get_operatingsystem_ids(module, theforeman, operatingsystems) 230 | 231 | if template_kind_name: 232 | res = get_resources(resource_type='template_kinds', 233 | resource_func=theforeman.search_template_kind, 234 | resource_specs=[template_kind_name]) 235 | if res: 236 | data['template_kind_id'] = res[0]["id"] 237 | 238 | if not snippet and operatingsystems is not None: 239 | data['operatingsystems'] = get_resources(resource_type='operatingsystem', 240 | resource_func=theforeman.search_operatingsystem, 241 | resource_specs=operatingsystems, 242 | search_field='title') 243 | 244 | if not config_template: 245 | try: 246 | config_template = theforeman.create_config_template(data=data) 247 | return True, config_template 248 | except ForemanError as e: 249 | module.fail_json(msg='Could not create config template: {0}'.format(e.message)) 250 | 251 | if not templates_equal(data, config_template): 252 | try: 253 | if config_template['locked']: 254 | unlock = {'id': config_template.get('id'), 'locked': False} 255 | theforeman.update_config_template(id=config_template.get('id'), data=unlock) 256 | if data.get('locked', config_template['locked']): 257 | data['locked'] = False 258 | theforeman.update_config_template(id=config_template.get('id'), data=data) 259 | data['locked'] = True 260 | config_template = theforeman.update_config_template(id=config_template.get('id'), data=data) 261 | return True, config_template 262 | except ForemanError as e: 263 | module.fail_json(msg='Could not update config template: {0}'.format(e.message)) 264 | 265 | return False, config_template 266 | 267 | 268 | def main(): 269 | global module 270 | module = AnsibleModule( 271 | argument_spec=dict( 272 | audit_comment=dict(type='str', required=False), 273 | name=dict(type='str', required=True), 274 | locked=dict(type='bool', required=False), 275 | operatingsystems=dict(type='list', required=False), 276 | template=dict(type='str', required=False), 277 | template_file=dict(type='str', required=False), 278 | template_kind_name=dict(type='str', required=False), 279 | snippet=dict(type='bool', required=False), 280 | organizations=dict(type='list', required=False), 281 | locations=dict(type='list', required=False), 282 | state=dict(type='str', default='present', choices=['present', 'absent']), 283 | foreman_host=dict(type='str', default='127.0.0.1'), 284 | foreman_port=dict(type='str', default='443'), 285 | foreman_user=dict(type='str', required=True), 286 | foreman_pass=dict(type='str', required=True, no_log=True), 287 | foreman_ssl=dict(type='bool', default=True) 288 | ), 289 | ) 290 | 291 | if not foremanclient_found: 292 | module.fail_json(msg='python-foreman module is required. See https://github.com/Nosmoht/python-foreman.') 293 | if has_import_error: 294 | module.fail_json(msg=import_error_msg) 295 | 296 | changed, config_template = ensure() 297 | module.exit_json(changed=changed, config_template=config_template) 298 | 299 | 300 | from ansible.module_utils.basic import * 301 | 302 | if __name__ == '__main__': 303 | main() 304 | -------------------------------------------------------------------------------- /foreman_domain.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # -*- coding: utf-8 -*- 3 | # 4 | # Ansible module to manage Foreman domain resources. 5 | # 6 | # This module is free software: you can redistribute it and/or modify 7 | # it under the terms of the GNU General Public License as published by 8 | # the Free Software Foundation, either version 3 of the License, or 9 | # (at your option) any later version. 10 | # 11 | # This software is distributed in the hope that it will be useful, 12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | # GNU General Public License for more details. 15 | # 16 | # You should have received a copy of the GNU General Public License 17 | # along with this software. If not, see . 18 | 19 | DOCUMENTATION = ''' 20 | --- 21 | module: foreman_domain 22 | short_description: Manage Foreman Domains using Foreman API v2 23 | description: 24 | - Create and delete Foreman Domain using Foreman API v2 25 | options: 26 | name: 27 | description: Domain name 28 | required: true 29 | fullname: 30 | description: Description of the domain 31 | required: false 32 | default: None 33 | dns_proxy: 34 | description: dns_proxy 35 | state: 36 | description: Domain state 37 | required: false 38 | default: present 39 | choices: ["present", "absent"] 40 | organizations: 41 | description: List of organizations the domain should be assigned to 42 | required: false 43 | locations: 44 | description: List of locations the domain should be assigned to 45 | required: false 46 | foreman_host: 47 | description: Hostname or IP address of Foreman system 48 | required: false 49 | default: 127.0.0.1 50 | foreman_port: 51 | description: Port of Foreman API 52 | required: false 53 | default: 443 54 | foreman_user: 55 | description: Username to be used to authenticate on Foreman 56 | required: true 57 | default: None 58 | foreman_pass: 59 | description: Password to be used to authenticate user on Foreman 60 | required: true 61 | default: None 62 | foreman_ssl: 63 | description: Enable SSL when connecting to Foreman API 64 | required: false 65 | default: true 66 | notes: 67 | - Requires the python-foreman package to be installed. See https://github.com/Nosmoht/python-foreman. 68 | version_added: "2.0" 69 | author: "Thomas Krahn (@nosmoht)" 70 | ''' 71 | 72 | EXAMPLES = ''' 73 | - name: Ensure example.com 74 | foreman_domain: 75 | name: example.com 76 | fullname: Example domain 77 | state: present 78 | organizations: 79 | - Torchlight 80 | locations: 81 | - Cardiff 82 | - London 83 | foreman_host: 127.0.0.1 84 | foreman_port: 443 85 | foreman_user: admin 86 | foreman_pass: secret 87 | ''' 88 | 89 | try: 90 | from foreman.foreman import * 91 | except ImportError: 92 | foremanclient_found = False 93 | else: 94 | foremanclient_found = True 95 | 96 | try: 97 | from ansible.module_utils.foreman_utils import * 98 | 99 | has_import_error = False 100 | except ImportError as e: 101 | has_import_error = True 102 | import_error_msg = str(e) 103 | 104 | 105 | def domains_equal(data, domain, comparable_keys): 106 | if not all(data.get(key, None) == domain.get(key, None) for key in comparable_keys): 107 | return False 108 | if not organizations_equal(data, domain): 109 | return False 110 | if not locations_equal(data, domain): 111 | return False 112 | return True 113 | 114 | 115 | def get_resources(resource_type, resource_specs, theforeman): 116 | result = [] 117 | for item in resource_specs: 118 | search_data = dict() 119 | if isinstance(item, dict): 120 | for key in item: 121 | search_data[key] = item[key] 122 | else: 123 | search_data['name'] = item 124 | try: 125 | resource = theforeman.search_resource(resource_type=resource_type, data=search_data) 126 | if not resource: 127 | module.fail_json( 128 | msg='Could not find resource type {resource_type} defined as {spec}'.format( 129 | resource_type=resource_type, 130 | spec=item)) 131 | result.append(resource) 132 | except ForemanError as e: 133 | module.fail_json(msg='Could not search resource type {resource_type} defined as {spec}: {error}'.format( 134 | resource_type=resource_type, spec=item, error=e.message)) 135 | return result 136 | 137 | 138 | def ensure(module): 139 | comparable_keys = ['name', 'fullname'] 140 | name = module.params['name'] 141 | fullname = module.params['fullname'] 142 | state = module.params['state'] 143 | dns_proxy = module.params['dns_proxy'] 144 | organizations = module.params['organizations'] 145 | locations = module.params['locations'] 146 | 147 | theforeman = init_foreman_client(module) 148 | 149 | data = {'name': name} 150 | 151 | try: 152 | domain = theforeman.search_domain(data=data) 153 | if domain: 154 | domain = theforeman.get_domain(id=domain.get('id')) 155 | except ForemanError as e: 156 | module.fail_json(msg='Could not get domain: {0}'.format(e.message)) 157 | 158 | if organizations: 159 | data['organization_ids'] = get_organization_ids(module, theforeman, organizations) 160 | 161 | if locations: 162 | data['location_ids'] = get_location_ids(module, theforeman, locations) 163 | 164 | data['fullname'] = fullname 165 | if dns_proxy: 166 | data['dns_id'] = get_resources(resource_type='smart_proxies', resource_specs=[dns_proxy], 167 | theforeman=theforeman)[0].get('id') 168 | 169 | if not domain and state == 'present': 170 | try: 171 | domain = theforeman.create_domain(data=data) 172 | return True, domain 173 | except ForemanError as e: 174 | module.fail_json(msg='Could not create domain: {0}'.format(e.message)) 175 | 176 | if domain: 177 | if state == 'absent': 178 | try: 179 | domain = theforeman.delete_domain(id=domain.get('id')) 180 | return True, domain 181 | except ForemanError as e: 182 | module.fail_json(msg='Could not delete domain: {0}'.format(e.message)) 183 | 184 | if not domains_equal(data, domain, comparable_keys): 185 | try: 186 | domain = theforeman.update_domain(id=domain.get('id'), data=data) 187 | return True, domain 188 | except ForemanError as e: 189 | module.fail_json(msg='Could not update domain: {0}'.format(e.message)) 190 | 191 | return False, domain 192 | 193 | 194 | def main(): 195 | global module 196 | 197 | module = AnsibleModule( 198 | argument_spec=dict( 199 | name=dict(type='str', required=True), 200 | fullname=dict(type='str', required=False), 201 | dns_proxy=dict(type='str', required=False), 202 | state=dict(type='str', default='present', choices=['present', 'absent']), 203 | organizations=dict(type='list', required=False), 204 | locations=dict(type='list', required=False), 205 | foreman_host=dict(type='str', default='127.0.0.1'), 206 | foreman_port=dict(type='str', default='443'), 207 | foreman_user=dict(type='str', required=True), 208 | foreman_pass=dict(type='str', required=True, no_log=True), 209 | foreman_ssl=dict(type='bool', default=True) 210 | ), 211 | ) 212 | 213 | if not foremanclient_found: 214 | module.fail_json(msg='python-foreman module is required. See https://github.com/Nosmoht/python-foreman.') 215 | 216 | changed, domain = ensure(module) 217 | module.exit_json(changed=changed, domain=domain) 218 | 219 | 220 | from ansible.module_utils.basic import * 221 | 222 | if __name__ == '__main__': 223 | main() 224 | -------------------------------------------------------------------------------- /foreman_environment.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # -*- coding: utf-8 -*- 3 | # 4 | # Ansible module to manage Foreman environment resources. 5 | # 6 | # This module is free software: you can redistribute it and/or modify 7 | # it under the terms of the GNU General Public License as published by 8 | # the Free Software Foundation, either version 3 of the License, or 9 | # (at your option) any later version. 10 | # 11 | # This software is distributed in the hope that it will be useful, 12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | # GNU General Public License for more details. 15 | # 16 | # You should have received a copy of the GNU General Public License 17 | # along with this software. If not, see . 18 | 19 | DOCUMENTATION = ''' 20 | --- 21 | module: foreman_environment 22 | short_description: Manage Foreman Environment using Foreman API v2 23 | description: 24 | - Create and delete Foreman Environments using Foreman API v2 25 | options: 26 | name: 27 | description: Name of environment 28 | required: true 29 | state: 30 | description: State of environment 31 | required: false 32 | default: present 33 | choices: ["present", "absent"] 34 | organizations: 35 | description: List of organizations the environement should be assigned to 36 | required: false 37 | locations: 38 | description: List of locations the environement should be assigned to 39 | required: false 40 | foreman_host: 41 | description: Hostname or IP address of Foreman system 42 | required: false 43 | default: 127.0.0.1 44 | foreman_port: 45 | description: Port of Foreman API 46 | required: false 47 | default: 443 48 | foreman_user: 49 | description: Username to be used to authenticate on Foreman 50 | required: true 51 | foreman_pass: 52 | description: Password to be used to authenticate user on Foreman 53 | required: true 54 | foreman_ssl: 55 | description: Enable SSL when connecting to Foreman API 56 | required: false 57 | default: true 58 | notes: 59 | - Requires the python-foreman package to be installed. See https://github.com/Nosmoht/python-foreman. 60 | version_added: "2.0" 61 | author: "Thomas Krahn (@nosmoht)" 62 | ''' 63 | 64 | EXAMPLES = ''' 65 | - name: Ensure Production environment is present 66 | foreman_environment: 67 | name: Production 68 | state: present 69 | organizations: 70 | - Prod INC 71 | locations: 72 | - New York City 73 | - Washington DC 74 | foreman_host: 127.0.0.1 75 | foreman_port: 443 76 | foreman_user: admin 77 | foreman_pass: secret 78 | ''' 79 | 80 | try: 81 | from foreman.foreman import * 82 | 83 | foremanclient_found = True 84 | except ImportError: 85 | foremanclient_found = False 86 | 87 | def get_organization_ids(module, theforeman, organizations): 88 | result = [] 89 | for i in range(0, len(organizations)): 90 | try: 91 | organization = theforeman.search_organization(data={'name': organizations[i]}) 92 | if not organization: 93 | module.fail_json('Could not find Organization {0}'.format(organizations[i])) 94 | result.append(organization.get('id')) 95 | except ForemanError as e: 96 | module.fail_json('Could not get Organizations: {0}'.format(e.message)) 97 | return result 98 | 99 | 100 | def get_location_ids(module, theforeman, locations): 101 | result = [] 102 | for i in range(0, len(locations)): 103 | try: 104 | location = theforeman.search_location(data={'name':locations[i]}) 105 | if not location: 106 | module.fail_json('Could not find Location {0}'.format(locations[i])) 107 | result.append(location.get('id')) 108 | except ForemanError as e: 109 | module.fail_json('Could not get Locations: {0}'.format(e.message)) 110 | return result 111 | 112 | 113 | def ensure(module): 114 | name = module.params['name'] 115 | state = module.params['state'] 116 | organizations = module.params['organizations'] 117 | locations = module.params['locations'] 118 | 119 | foreman_host = module.params['foreman_host'] 120 | foreman_port = module.params['foreman_port'] 121 | foreman_user = module.params['foreman_user'] 122 | foreman_pass = module.params['foreman_pass'] 123 | foreman_ssl = module.params['foreman_ssl'] 124 | 125 | theforeman = Foreman(hostname=foreman_host, 126 | port=foreman_port, 127 | username=foreman_user, 128 | password=foreman_pass, 129 | ssl=foreman_ssl) 130 | 131 | data = {'name': name} 132 | 133 | try: 134 | env = theforeman.search_environment(data=data) 135 | except ForemanError as e: 136 | module.fail_json(msg='Could not get environment: {0}'.format(e.message)) 137 | 138 | if organizations: 139 | data['organization_ids'] = get_organization_ids(module, theforeman, organizations) 140 | 141 | if locations: 142 | data['location_ids'] = get_location_ids(module, theforeman, locations) 143 | 144 | 145 | if not env and state == 'present': 146 | try: 147 | env = theforeman.create_environment(data=data) 148 | return True, env 149 | except ForemanError as e: 150 | module.fail_json(msg='Could not create environment: {0}'.format(e.message)) 151 | 152 | if env and state == 'absent': 153 | try: 154 | env = theforeman.delete_environment(id=env.get('id')) 155 | return True, env 156 | except ForemanError as e: 157 | module.fail_json(msg='Could not delete environment: {0}'.format(e.message)) 158 | 159 | return False, env 160 | 161 | 162 | def main(): 163 | module = AnsibleModule( 164 | argument_spec=dict( 165 | name=dict(type='str', required=True), 166 | state=dict(type='str', default='present', choices=['present', 'absent']), 167 | organizations=dict(type='list', required=False), 168 | locations=dict(type='list', required=False), 169 | foreman_host=dict(type='str', default='127.0.0.1'), 170 | foreman_port=dict(type='str', default='443'), 171 | foreman_user=dict(type='str', required=True), 172 | foreman_pass=dict(type='str', required=True, no_log=True), 173 | foreman_ssl=dict(type='bool', default=True) 174 | ), 175 | ) 176 | 177 | if not foremanclient_found: 178 | module.fail_json(msg='python-foreman module is required. See https://github.com/Nosmoht/python-foreman.') 179 | 180 | changed, env = ensure(module) 181 | module.exit_json(changed=changed, environment=env) 182 | 183 | 184 | from ansible.module_utils.basic import * 185 | 186 | if __name__ == '__main__': 187 | main() -------------------------------------------------------------------------------- /foreman_external_usergroup.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # -*- coding: utf-8 -*- 3 | # 4 | # Ansible module to manage Foreman external_usergroup resources. 5 | # 6 | # This module is free software: you can redistribute it and/or modify 7 | # it under the terms of the GNU General Public License as published by 8 | # the Free Software Foundation, either version 3 of the License, or 9 | # (at your option) any later version. 10 | # 11 | # This software is distributed in the hope that it will be useful, 12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | # GNU General Public License for more details. 15 | # 16 | # You should have received a copy of the GNU General Public License 17 | # along with this software. If not, see . 18 | # 19 | # (C) 2017 Guido Günther 20 | 21 | DOCUMENTATION = ''' 22 | --- 23 | module: foreman_external_usergroup 24 | short_description: Manage Foreman External Usergroup using Foreman API v2 25 | description: 26 | - Create and delete Foreman External Usergroups using Foreman API v2 27 | options: 28 | name: 29 | description: name of the external usergroup 30 | required: true 31 | auth_source: 32 | description: auth source of this usergroup 33 | required: true 34 | usergroup: 35 | description: usergroup to link to. 36 | required: true 37 | state: 38 | description: State of usergroup 39 | required: false 40 | default: present 41 | choices: ["present", "absent"] 42 | foreman_host: 43 | description: Hostname or IP address of Foreman system 44 | required: false 45 | default: 127.0.0.1 46 | foreman_port: 47 | description: Port of Foreman API 48 | required: false 49 | default: 443 50 | foreman_user: 51 | description: Username to be used to authenticate on Foreman 52 | required: true 53 | foreman_pass: 54 | description: Password to be used to authenticate user on Foreman 55 | required: true 56 | foreman_ssl: 57 | description: Enable SSL when connecting to Foreman API 58 | required: false 59 | default: true 60 | notes: 61 | - Requires the python-foreman package to be installed. See https://github.com/Nosmoht/python-foreman. 62 | - This module does currently not update already existing groups 63 | version_added: "2.0" 64 | author: "Guido Gúnther (@agx)" 65 | ''' 66 | 67 | EXAMPLES = ''' 68 | - name: Ensure LDAP group wheel is linked to foreman usergroup admin 69 | foreman_external_usergroup: 70 | name: wheel 71 | auth_source: LDAP-Server 72 | usergroup: admin 73 | state: present 74 | foreman_host: 127.0.0.1 75 | foreman_port: 443 76 | foreman_user: admin 77 | foreman_pass: secret 78 | ''' 79 | 80 | try: 81 | from foreman.foreman import * 82 | foremanclient_found = True 83 | except ImportError: 84 | foremanclient_found = False 85 | 86 | 87 | def get_id(module, theforeman, res_type, name, field='name'): 88 | result = None 89 | try: 90 | searcher = getattr(theforeman, "search_{0}".format(res_type)) 91 | except AttributeError: 92 | module.fail_json(msg="Don't know how to search for {0}".format(res_type)) 93 | 94 | try: 95 | res = searcher(data={field: name}) 96 | if not res: 97 | module.fail_json(msg="Could not find '{0}' of type {1}".format(name, res_type)) 98 | result = res.get('id') 99 | except ForemanError as e: 100 | module.fail_json(msg="Could not get '{0}' of type {1}: {2}".format(name, 101 | res_type, 102 | e.message)) 103 | return result 104 | 105 | 106 | def ensure(module): 107 | name = module.params['name'] 108 | state = module.params['state'] 109 | auth_source = module.params['auth_source'] 110 | usergroup = module.params['usergroup'] 111 | ext_group = None 112 | 113 | foreman_host = module.params['foreman_host'] 114 | foreman_port = module.params['foreman_port'] 115 | foreman_user = module.params['foreman_user'] 116 | foreman_pass = module.params['foreman_pass'] 117 | foreman_ssl = module.params['foreman_ssl'] 118 | 119 | theforeman = Foreman(hostname=foreman_host, 120 | port=foreman_port, 121 | username=foreman_user, 122 | password=foreman_pass, 123 | ssl=foreman_ssl) 124 | try: 125 | group = theforeman.search_usergroup({'name': usergroup}) 126 | except ForemanError as e: 127 | module.fail_json(msg='Could not get group usergroup: {0}'.format(e.message)) 128 | 129 | data = dict(name=name) 130 | try: 131 | ext_groups = theforeman.get_external_usergroups(group['id']) 132 | except ForemanError as e: 133 | module.fail_json(msg='Could not get external usergroups for {0}: {1}'.format(name, e.message)) 134 | 135 | for e in ext_groups: 136 | if e['name'] == name: 137 | ext_group = e 138 | break 139 | 140 | if not ext_group and state == 'present': 141 | data['usergroup_id'] = group['id'] 142 | data['auth_source_id'] = get_id(module, theforeman, 'auth_source_ldap', auth_source) 143 | 144 | try: 145 | ext_group = theforeman.create_external_usergroup(group['id'], data=data) 146 | return True, ext_group 147 | except ForemanError as e: 148 | module.fail_json(msg='Could not create external usergroup: {0}'.format(e.message)) 149 | 150 | if ext_group and state == 'absent': 151 | try: 152 | ext_group = theforeman.delete_external_usergroup(group_id=group['id'], ext_group_id=ext_group['id']) 153 | except ForemanError as e: 154 | module.fail_json(msg='Could not delete external usergroup: {0}'.format(e.message)) 155 | return True, ext_group 156 | 157 | return False, ext_group 158 | 159 | 160 | def main(): 161 | module = AnsibleModule( 162 | argument_spec=dict( 163 | state=dict(type='str', default='present', choices=['present', 'absent']), 164 | name=dict(type='str', required=True), 165 | usergroup=dict(type='str', required=True), 166 | auth_source=dict(type='str', required=True), 167 | foreman_host=dict(type='str', default='127.0.0.1'), 168 | foreman_port=dict(type='str', default='443'), 169 | foreman_user=dict(type='str', required=True), 170 | foreman_pass=dict(type='str', required=True, no_log=True), 171 | foreman_ssl=dict(type='bool', default=True) 172 | ), 173 | ) 174 | 175 | if not foremanclient_found: 176 | module.fail_json(msg='python-foreman module is required. See https://github.com/Nosmoht/python-foreman.') 177 | 178 | changed, usergroup = ensure(module) 179 | module.exit_json(changed=changed, usergroup=usergroup) 180 | 181 | 182 | from ansible.module_utils.basic import * 183 | 184 | if __name__ == '__main__': 185 | main() 186 | -------------------------------------------------------------------------------- /foreman_filter.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # -*- coding: utf-8 -*- 3 | # 4 | # Ansible module to manage Foreman filter resources. 5 | # 6 | # This module is free software: you can redistribute it and/or modify 7 | # it under the terms of the GNU General Public License as published by 8 | # the Free Software Foundation, either version 3 of the License, or 9 | # (at your option) any later version. 10 | # 11 | # This software is distributed in the hope that it will be useful, 12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | # GNU General Public License for more details. 15 | # 16 | # You should have received a copy of the GNU General Public License 17 | # along with this software. If not, see . 18 | # 19 | # (C) 2017 Guido Günther 20 | 21 | DOCUMENTATION = ''' 22 | --- 23 | module: foreman_filter 24 | short_description: Manage Foreman Filter using Foreman API v2 25 | description: 26 | - Create and delete Foreman Filters using Foreman API v2 27 | options: 28 | role: 29 | description: role this filter belongs tue 30 | required: true 31 | resource_type: 32 | description: resource type this permission affects 33 | required: true 34 | permissions: 35 | description: List of permissions 36 | required: true 37 | state: 38 | description: State of filter 39 | required: false 40 | default: present 41 | choices: ["present", "absent"] 42 | foreman_host: 43 | description: Hostname or IP address of Foreman system 44 | required: false 45 | default: 127.0.0.1 46 | foreman_port: 47 | description: Port of Foreman API 48 | required: false 49 | default: 443 50 | foreman_user: 51 | description: Username to be used to authenticate on Foreman 52 | required: true 53 | foreman_pass: 54 | description: Password to be used to authenticate user on Foreman 55 | required: true 56 | foreman_ssl: 57 | description: Enable SSL when connecting to Foreman API 58 | required: false 59 | default: true 60 | notes: 61 | - Requires the python-foreman package to be installed. See https://github.com/Nosmoht/python-foreman. 62 | version_added: "2.0" 63 | author: "Thomas Krahn (@nosmoht)" 64 | ''' 65 | 66 | EXAMPLES = ''' 67 | - name: Ensure Production filter is present 68 | foreman_filter: 69 | role: "Host Power" 70 | resource: Host 71 | permissions: 72 | - power_hosts 73 | state: present 74 | foreman_host: 127.0.0.1 75 | foreman_port: 443 76 | foreman_user: admin 77 | foreman_pass: secret 78 | ''' 79 | 80 | try: 81 | from foreman.foreman import * 82 | 83 | foremanclient_found = True 84 | except ImportError: 85 | foremanclient_found = False 86 | 87 | def get_permission_ids(module, theforeman, resource_type, permissions): 88 | result = [] 89 | for i in range(0, len(permissions)): 90 | try: 91 | permission = theforeman.search_permission(data={'resource_type': resource_type, 92 | 'name': permissions[i]}) 93 | if not permission: 94 | module.fail_json(msg='Could not find Permission {0} for {1}'.format(permissions[i], 95 | resource_type)) 96 | result.append(permission.get('id')) 97 | except ForemanError as e: 98 | module.fail_json(msg='Could not get Permissions: {0}'.format(e.message)) 99 | return result 100 | 101 | 102 | def get_role_id(module, theforeman, rolename): 103 | try: 104 | role = theforeman.search_role(data={'name': rolename}) 105 | if not role: 106 | module.fail_json(msg='Could not find role {0}'.format(rolename)) 107 | i = role.get('id') 108 | if not i: 109 | module.fail_json(msg='Can\'t find id for {0} in {1}'.format(rolename, role)) 110 | except ForemanError as e: 111 | module.fail_json(msg='Could not get role: {0}'.format(e.message)) 112 | return role['id'] 113 | 114 | 115 | def ensure(module): 116 | state = module.params['state'] 117 | role = module.params['role'] 118 | resource_type = module.params['resource_type'] 119 | permissions = module.params['permissions'] 120 | 121 | foreman_host = module.params['foreman_host'] 122 | foreman_port = module.params['foreman_port'] 123 | foreman_user = module.params['foreman_user'] 124 | foreman_pass = module.params['foreman_pass'] 125 | foreman_ssl = module.params['foreman_ssl'] 126 | 127 | theforeman = Foreman(hostname=foreman_host, 128 | port=foreman_port, 129 | username=foreman_user, 130 | password=foreman_pass, 131 | ssl=foreman_ssl) 132 | 133 | data = dict() 134 | 135 | data['permission_ids'] = get_permission_ids(module, theforeman, resource_type, permissions) 136 | data['role_id'] = get_role_id(module, theforeman, role) 137 | 138 | filters = theforeman.search_filter(data={'role_id': data['role_id']}) 139 | if not filters and state == 'present': 140 | try: 141 | filtr = theforeman.create_filter(data=data) 142 | return True, filtr 143 | except ForemanError as e: 144 | module.fail_json(msg='Could not create filter: {0}'.format(e.message)) 145 | 146 | if filters: 147 | if type(filters) != type([]): 148 | filters = [filters] 149 | 150 | if state == 'present': 151 | for f in filters: 152 | if sorted(p['id'] for p in f['permissions']) == sorted(data['permission_ids']): 153 | return False, f 154 | try: 155 | # We have no way to decide if perms should be updated so best 156 | # we can do is create a new one 157 | filtr = theforeman.create_filter(data=data) 158 | return True, filtr 159 | except ForemanError as e: 160 | module.fail_json(msg='Could not create filter: {0}'.format(e.message)) 161 | elif state == 'absent': 162 | for f in filters: 163 | if sorted(p['id'] for p in f['permissions']) == sorted(data['permission_ids']): 164 | try: 165 | filtr = theforeman.delete_filter(id=f['id']) 166 | return True, filtr 167 | except ForemanError as e: 168 | module.fail_json(msg='Could not delete filter: {0}'.format(e.message)) 169 | return False, filtr 170 | 171 | 172 | def main(): 173 | module = AnsibleModule( 174 | argument_spec=dict( 175 | state=dict(type='str', default='present', choices=['present', 'absent']), 176 | role=dict(type='str', required=True), 177 | resource_type=dict(type='str', required=True), 178 | permissions=dict(type='list', required=True), 179 | foreman_host=dict(type='str', default='127.0.0.1'), 180 | foreman_port=dict(type='str', default='443'), 181 | foreman_user=dict(type='str', required=True), 182 | foreman_pass=dict(type='str', required=True, no_log=True), 183 | foreman_ssl=dict(type='bool', default=True) 184 | ), 185 | ) 186 | 187 | if not foremanclient_found: 188 | module.fail_json(msg='python-foreman module is required. See https://github.com/Nosmoht/python-foreman.') 189 | 190 | changed, filtr = ensure(module) 191 | module.exit_json(changed=changed, filter=filtr) 192 | 193 | 194 | from ansible.module_utils.basic import * 195 | 196 | if __name__ == '__main__': 197 | main() 198 | -------------------------------------------------------------------------------- /foreman_global_parameter.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # -*- coding: utf-8 -*- 3 | # 4 | # Ansible module to manage Foreman global parameters. 5 | # 6 | # This module is free software: you can redistribute it and/or modify 7 | # it under the terms of the GNU General Public License as published by 8 | # the Free Software Foundation, either version 3 of the License, or 9 | # (at your option) any later version. 10 | # 11 | # This software is distributed in the hope that it will be useful, 12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | # GNU General Public License for more details. 15 | # 16 | # You should have received a copy of the GNU General Public License 17 | # along with this software. If not, see . 18 | 19 | DOCUMENTATION = ''' 20 | --- 21 | module: foreman_global_parameter 22 | short_description: Manage Foreman global parameter using Foreman API v2 23 | description: 24 | - Update Foreman global parameter using Foreman API v2 25 | options: 26 | name: 27 | description: Global parameter name 28 | required: true 29 | value: 30 | description: parameter value 31 | required: true 32 | foreman_host: 33 | description: Hostname or IP address of Foreman system 34 | required: false 35 | default: 127.0.0.1 36 | foreman_port: 37 | description: Port of Foreman API 38 | required: false 39 | default: 443 40 | foreman_user: 41 | description: Username to be used to authenticate on Foreman 42 | required: true 43 | foreman_pass: 44 | description: Password to be used to authenticate user on Foreman 45 | required: true 46 | foreman_ssl: 47 | description: Enable SSL when connecting to Foreman API 48 | required: false 49 | default: true 50 | notes: 51 | - Requires the python-foreman package to be installed. See https://github.com/Nosmoht/python-foreman. 52 | version_added: "2.0" 53 | author: "Radim Janca " 54 | ''' 55 | 56 | EXAMPLES = ''' 57 | - name: Ensure Global parameter 58 | foreman_global_parameter: 59 | name: baud 60 | value: 115200 61 | state: present 62 | foreman_host: foreman.example.com 63 | foreman_port: 443 64 | foreman_user: admin 65 | foreman_pass: secret 66 | ''' 67 | 68 | try: 69 | from foreman.foreman import * 70 | foremanclient_found = True 71 | except ImportError: 72 | foremanclient_found = False 73 | 74 | def ensure(module): 75 | global theforeman 76 | 77 | name = module.params['name'] 78 | value = module.params['value'] 79 | state = module.params['state'] 80 | global_parameter = None 81 | 82 | foreman_host = module.params['foreman_host'] 83 | foreman_port = module.params['foreman_port'] 84 | foreman_user = module.params['foreman_user'] 85 | foreman_pass = module.params['foreman_pass'] 86 | foreman_ssl = module.params['foreman_ssl'] 87 | 88 | theforeman = Foreman(hostname=foreman_host, 89 | port=foreman_port, 90 | username=foreman_user, 91 | password=foreman_pass, 92 | ssl=foreman_ssl) 93 | 94 | data = {'name': name} 95 | 96 | try: 97 | global_parameter = theforeman.search_common_parameter(data=data) 98 | except ForemanError as e: 99 | module.fail_json(msg='Could not get global parameter: {0}'.format(e.message)) 100 | 101 | data['value'] = value 102 | 103 | if state == 'present': 104 | if not global_parameter: 105 | try: 106 | global_parameter = theforeman.create_common_parameter(data=data) 107 | return True, global_parameter 108 | except ForemanError as e: 109 | module.fail_json(msg='Could not create global parameter: {0}'.format(e.message)) 110 | else: 111 | if data['value'] != global_parameter['value']: 112 | try: 113 | global_parameter = theforeman.update_resource(resource_type='common_parameters', 114 | resource_id=global_parameter.get('id'), 115 | data=data) 116 | return True, global_parameter 117 | except ForemanError as e: 118 | module.fail_json(msg='Could not update global parameter: {0}'.format(e.message)) 119 | 120 | if state == 'absent': 121 | if global_parameter: 122 | try: 123 | global_parameter = theforeman.delete_common_parameter(id=global_parameter.get('id')) 124 | return True, global_parameter 125 | except ForemanError as e: 126 | module.fail_json(msg='Could not remove global parameter: {0}'.format(e.message)) 127 | 128 | return False, global_parameter 129 | 130 | def main(): 131 | global module 132 | 133 | module = AnsibleModule( 134 | argument_spec=dict( 135 | name=dict(type='str', required=True), 136 | value=dict(type='str', required=True), 137 | state=dict(type='str', default='present', choices=['present', 'absent']), 138 | foreman_host=dict(type='str', default='127.0.0.1'), 139 | foreman_port=dict(type='str', default='443'), 140 | foreman_user=dict(type='str', required=True), 141 | foreman_pass=dict(type='str', required=True, no_log=True), 142 | foreman_ssl=dict(type='bool', default=True) 143 | ), 144 | ) 145 | 146 | if not foremanclient_found: 147 | module.fail_json(msg='python-foreman module is required. See https://github.com/Nosmoht/python-foreman.') 148 | 149 | changed, global_parameter = ensure(module) 150 | module.exit_json(changed=changed, global_parameter=global_parameter) 151 | 152 | # import module snippets 153 | from ansible.module_utils.basic import * 154 | 155 | if __name__ == '__main__': 156 | main() 157 | -------------------------------------------------------------------------------- /foreman_host_check.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # -*- coding: utf-8 -*- 3 | # 4 | # Ansible module to get status form Foreman host resources. 5 | # 6 | # This module is free software: you can redistribute it and/or modify 7 | # it under the terms of the GNU General Public License as published by 8 | # the Free Software Foundation, either version 3 of the License, or 9 | # (at your option) any later version. 10 | # 11 | # This software is distributed in the hope that it will be useful, 12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | # GNU General Public License for more details. 15 | # 16 | # You should have received a copy of the GNU General Public License 17 | # along with this software. If not, see . 18 | 19 | DOCUMENTATION = ''' 20 | --- 21 | module: foreman_host 22 | short_description: Get Foreman host info using Foreman API v2 23 | description: 24 | - Get Foreman host info using Foreman API v2 25 | options: 26 | name: 27 | description: Hostgroup name 28 | required: true 29 | default: None 30 | domain: 31 | description: Domain name 32 | required: false 33 | default: None 34 | foreman_host: 35 | description: Hostname or IP address of Foreman system 36 | required: false 37 | default: 127.0.0.1 38 | foreman_port: 39 | description: Port of Foreman API 40 | required: false 41 | default: 443 42 | foreman_user: 43 | description: Username to be used to authenticate on Foreman 44 | required: true 45 | foreman_pass: 46 | description: Password to be used to authenticate user on Foreman 47 | required: true 48 | foreman_ssl: 49 | description: Enable SSL when connecting to Foreman API 50 | required: false 51 | default: true 52 | notes: 53 | - Requires the python-foreman package to be installed. See https://github.com/Nosmoht/python-foreman. 54 | version_added: "2.0" 55 | author: "Thomas Krahn (@nosmoht)" 56 | ''' 57 | 58 | 59 | try: 60 | from foreman.foreman import * 61 | except ImportError: 62 | foremanclient_found = False 63 | else: 64 | foremanclient_found = True 65 | 66 | try: 67 | from ansible.module_utils.foreman_utils import * 68 | 69 | has_import_error = False 70 | except ImportError as e: 71 | has_import_error = True 72 | import_error_msg = str(e) 73 | 74 | 75 | def ensure(): 76 | name = module.params['name'] 77 | domain_name = module.params['domain'] 78 | 79 | theforeman = init_foreman_client(module) 80 | 81 | if domain_name: 82 | if domain_name in name: 83 | host_name = name 84 | else: 85 | host_name = '{name}.{domain}'.format(name=name, domain=domain_name) 86 | else: 87 | host_name = name 88 | 89 | data = dict(name=host_name) 90 | 91 | try: 92 | host = theforeman.search_host(data=data) 93 | if host: 94 | host = theforeman.get_host(id=host.get('id')) 95 | return False, host 96 | except ForemanError as e: 97 | module.fail_json(msg='Error while searching host: {0}'.format(e.message)) 98 | 99 | module.fail_json(msg="Host '{host}' does not exist.'".format(host=host_name)) 100 | 101 | 102 | def main(): 103 | global module 104 | module = AnsibleModule( 105 | argument_spec=dict( 106 | name=dict(type='str', required=True), 107 | domain=dict(type='str', default=None), 108 | foreman_host=dict(type='str', default='127.0.0.1'), 109 | foreman_port=dict(type='str', default='443'), 110 | foreman_user=dict(type='str', required=True), 111 | foreman_pass=dict(type='str', required=True, no_log=True), 112 | foreman_ssl=dict(type='bool', default=True) 113 | ), 114 | ) 115 | 116 | if not foremanclient_found: 117 | module.fail_json( 118 | msg='python-foreman module is required. See https://github.com/Nosmoht/python-foreman.') 119 | 120 | changed, host = ensure() 121 | module.exit_json(changed=changed, host=host) 122 | 123 | 124 | from ansible.module_utils.basic import * 125 | 126 | if __name__ == '__main__': 127 | main() 128 | -------------------------------------------------------------------------------- /foreman_hostgroup.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # -*- coding: utf-8 -*- 3 | # 4 | # Ansible module to manage Foreman hostgroup resources. 5 | # 6 | # This module is free software: you can redistribute it and/or modify 7 | # it under the terms of the GNU General Public License as published by 8 | # the Free Software Foundation, either version 3 of the License, or 9 | # (at your option) any later version. 10 | # 11 | # This software is distributed in the hope that it will be useful, 12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | # GNU General Public License for more details. 15 | # 16 | # You should have received a copy of the GNU General Public License 17 | # along with this software. If not, see . 18 | 19 | DOCUMENTATION = ''' 20 | --- 21 | module: foreman_hostgroup 22 | short_description: Manage Foreman Hostgroup using Foreman API v2 23 | description: 24 | - Manage Foreman Hostgroup using Foreman API v2 25 | options: 26 | architecture: 27 | description: Architecture name 28 | required: False 29 | default: None 30 | domain: 31 | description: Domain name 32 | required: False 33 | default: None 34 | environment: 35 | description: Environment name 36 | required: False 37 | default: None 38 | medium: 39 | description: Medium name 40 | required: False 41 | default: None 42 | name: 43 | description: Hostgroup name 44 | required: True 45 | operatingsystem: 46 | description: Operatingsystem name 47 | required: False 48 | default: None 49 | parameters: 50 | description: List of parameters and values 51 | required: false 52 | default: None 53 | partition_table: 54 | description: Partition table name 55 | required: False 56 | default: None 57 | pxe_loader: 58 | description: PXE Loader 59 | required: False 60 | default: None 61 | realm: 62 | description: Realm name 63 | required: false 64 | default: None 65 | root_pass: 66 | description: root password 67 | required: false 68 | default: None 69 | force_update: 70 | description: forces update of hostgroup, usefull when in need of password update 71 | required: false 72 | default: false 73 | smart_proxy: 74 | description: Smart Proxy name 75 | required: False 76 | default: None 77 | subnet: 78 | description: Subnet name 79 | required: False 80 | default: None 81 | state: 82 | description: Hostgroup state 83 | required: false 84 | default: present 85 | choices: ["present", "absent"] 86 | locations: List of locations the subnet should be assigned to 87 | required: false 88 | default: None 89 | organizations: List of organizations the subnet should be assigned to 90 | required: false 91 | default: None 92 | foreman_host: 93 | description: Hostname or IP address of Foreman system 94 | required: false 95 | default: 127.0.0.1 96 | foreman_port: 97 | description: Port of Foreman API 98 | required: false 99 | default: 443 100 | foreman_user: 101 | description: Username to be used to authenticate on Foreman 102 | required: true 103 | foreman_pass: 104 | description: Password to be used to authenticate user on Foreman 105 | required: true 106 | foreman_ssl: 107 | description: Enable SSL when connecting to Foreman API 108 | required: false 109 | default: true 110 | notes: 111 | - Requires the python-foreman package to be installed. See https://github.com/Nosmoht/python-foreman. 112 | version_added: "2.0" 113 | author: "Thomas Krahn (@nosmoht)" 114 | ''' 115 | 116 | EXAMPLES = ''' 117 | - name: Ensure Hostgroup 118 | foreman_hostgroup: 119 | name: MyHostgroup 120 | state: present 121 | architecture: x86_64 122 | domain: MyDomain 123 | operatingsystem: MyOS 124 | subnet: MySubnet 125 | foreman_host: 127.0.0.1 126 | foreman_port: 443 127 | foreman_user: admin 128 | foreman_pass: secret 129 | ''' 130 | 131 | try: 132 | from foreman.foreman import * 133 | except ImportError: 134 | foremanclient_found = False 135 | else: 136 | foremanclient_found = True 137 | 138 | try: 139 | from ansible.module_utils.foreman_utils import * 140 | 141 | has_import_error = False 142 | except ImportError as e: 143 | has_import_error = True 144 | import_error_msg = str(e) 145 | 146 | 147 | def get_resource(module, resource_type, resource_func, resource_name, search_title=False): 148 | """ 149 | Look for a resource within Foreman Database. Return the resource if found or fail. 150 | If the Resource could not be found by name search by title. 151 | 152 | :param module: 153 | :param resource_type: 154 | :param resource_func: 155 | :param resource_name: 156 | :return: 157 | """ 158 | try: 159 | result = resource_func(data=dict(name=resource_name)) 160 | if not result and search_title: 161 | result = resource_func(data=dict(title=resource_name)) 162 | if not result: 163 | module.fail_json(msg='{0} {1} not found'.format(resource_type, resource_name)) 164 | except ForemanError as e: 165 | module.fail_json(msg='Error while getting {0}: {1}'.format(resource_type, e.message)) 166 | return result 167 | 168 | 169 | def split_parent(name): 170 | """ 171 | Split hostgroup name in parent part and name: 172 | 173 | >>> split_parent("a/b/c") 174 | ('c', 'a/b') 175 | """ 176 | if '/' in name: 177 | parent, name = name.rsplit('/', 1) 178 | else: 179 | return name, None 180 | return name, parent 181 | 182 | 183 | def hostgroups_equal(data, hostgroup): 184 | comparable_keys = set(data.keys()).intersection(set( 185 | ['name', 'title', 'architecture_id', 'compute_profile_id', 'domain_id', 'environment_id', 'medium_id', 186 | 'operatingsystem_id', 'ptable_id', 'realm_id', 'puppet_proxy_id', 'subnet_id', 'parent_id', 'pxe_loader'])) 187 | if not all(str(data.get(key, None)) == str(hostgroup.get(key, None)) for key in comparable_keys): 188 | return False 189 | if not organizations_equal(data, hostgroup): 190 | return False 191 | if not locations_equal(data, hostgroup): 192 | return False 193 | return True 194 | 195 | 196 | def ensure(module): 197 | changed = False 198 | full_name = module.params['name'] 199 | short_name, parent_name = split_parent(full_name) 200 | architecture_name = module.params['architecture'] 201 | compute_profile_name = module.params['compute_profile'] 202 | domain_name = module.params['domain'] 203 | environment_name = module.params['environment'] 204 | medium_name = module.params['medium'] 205 | operatingsystem_name = module.params['operatingsystem'] 206 | partition_table_name = module.params['partition_table'] 207 | pxe_loader = module.params['pxe_loader'] 208 | realm_name = module.params['realm'] 209 | root_pass = module.params['root_pass'] 210 | smart_proxy_name = module.params['smart_proxy'] 211 | subnet_name = module.params['subnet'] 212 | locations = module.params['locations'] 213 | organizations = module.params['organizations'] 214 | state = module.params['state'] 215 | parameters = module.params['parameters'] 216 | force_update = module.params['force_update'] 217 | 218 | theforeman = init_foreman_client(module) 219 | 220 | data = {'title': full_name, 'name': short_name} 221 | 222 | try: 223 | hostgroup = theforeman.search_hostgroup(data=data) 224 | if hostgroup: 225 | hostgroup = theforeman.get_hostgroup(id=hostgroup.get('id')) 226 | except ForemanError as e: 227 | module.fail_json(msg='Could not get hostgroup: {0}'.format(e.message)) 228 | 229 | if state == 'absent': 230 | if hostgroup: 231 | try: 232 | hostgroup = theforeman.delete_hostgroup(id=hostgroup.get('id')) 233 | return True, hostgroup 234 | except ForemanError as e: 235 | module.fail_json(msg='Could not delete hostgroup: {0}'.format(e.message)) 236 | else: 237 | return False, hostgroup 238 | 239 | if organizations: 240 | data['organization_ids'] = get_organization_ids(module, theforeman, organizations) 241 | 242 | if locations: 243 | data['location_ids'] = get_location_ids(module, theforeman, locations) 244 | 245 | # Architecture 246 | if architecture_name: 247 | architecture = get_resource(module=module, 248 | resource_type=ARCHITECTURE, 249 | resource_func=theforeman.search_architecture, 250 | resource_name=architecture_name) 251 | data['architecture_id'] = str(architecture.get('id')) 252 | 253 | # Compute Profile 254 | if compute_profile_name: 255 | compute_profile = get_resource(module=module, 256 | resource_type=COMPUTE_PROFILE, 257 | resource_func=theforeman.search_compute_profile, 258 | resource_name=compute_profile_name) 259 | data['compute_profile_id'] = str(compute_profile.get('id')) 260 | 261 | # Domain 262 | if domain_name: 263 | domain = get_resource(module=module, 264 | resource_type=DOMAIN, 265 | resource_func=theforeman.search_domain, 266 | resource_name=domain_name) 267 | data['domain_id'] = str(domain.get('id')) 268 | 269 | # Environment 270 | if environment_name: 271 | environment = get_resource(module=module, 272 | resource_type=ENVIRONMENT, 273 | resource_func=theforeman.search_environment, 274 | resource_name=environment_name) 275 | data['environment_id'] = str(environment.get('id')) 276 | 277 | # Medium 278 | if medium_name: 279 | medium = get_resource(module=module, 280 | resource_type=MEDIUM, 281 | resource_func=theforeman.search_medium, 282 | resource_name=medium_name) 283 | data['medium_id'] = str(medium.get('id')) 284 | 285 | # Operatingssystem 286 | if operatingsystem_name: 287 | operatingsystem = get_resource(module=module, 288 | resource_type=OPERATINGSYSTEM, 289 | resource_func=theforeman.search_operatingsystem, 290 | resource_name=operatingsystem_name, 291 | search_title=True) 292 | data['operatingsystem_id'] = str(operatingsystem.get('id')) 293 | 294 | # Partition Table 295 | if partition_table_name: 296 | partition_table = get_resource(module=module, 297 | resource_type=PARTITION_TABLE, 298 | resource_func=theforeman.search_partition_table, 299 | resource_name=partition_table_name) 300 | data['ptable_id'] = str(partition_table.get('id')) 301 | 302 | # PXE loader 303 | if pxe_loader: 304 | data['pxe_loader'] = pxe_loader 305 | 306 | # Realm 307 | if realm_name: 308 | realm = get_resource(module=module, 309 | resource_type=REALM, 310 | resource_func=theforeman.search_realm, 311 | resource_name=realm_name) 312 | data['realm_id'] = str(realm.get('id')) 313 | # Root password 314 | if root_pass: 315 | data['root_pass'] = root_pass 316 | 317 | # Smart Proxy 318 | if smart_proxy_name: 319 | smart_proxy = get_resource(module=module, 320 | resource_type=SMART_PROXY, 321 | resource_func=theforeman.search_smart_proxy, 322 | resource_name=smart_proxy_name) 323 | data['puppet_proxy_id'] = str(smart_proxy.get('id')) 324 | 325 | # Subnet 326 | if subnet_name: 327 | subnet = get_resource(module=module, 328 | resource_type=SUBNET, 329 | resource_func=theforeman.search_subnet, 330 | resource_name=subnet_name) 331 | data['subnet_id'] = str(subnet.get('id')) 332 | 333 | # Parent 334 | if parent_name: 335 | parent = get_resource(module=module, 336 | resource_type=HOSTGROUP, 337 | resource_func=theforeman.search_hostgroup, 338 | search_title=True, 339 | resource_name=parent_name) 340 | data['parent_id'] = str(parent.get('id')) 341 | 342 | # state == present 343 | if not hostgroup: 344 | try: 345 | hostgroup = theforeman.create_hostgroup(data=data) 346 | changed = True 347 | except ForemanError as e: 348 | module.fail_json(msg='Could not create hostgroup: {0}'.format(e.message)) 349 | elif not hostgroups_equal(data, hostgroup) or force_update: 350 | try: 351 | hostgroup = theforeman.update_hostgroup(id=hostgroup.get('id'), data=data) 352 | changed = True 353 | except ForemanError as e: 354 | module.fail_json(msg='Could not update hostgroup: {0}'.format(e.message)) 355 | 356 | hostgroup_id = hostgroup.get('id') 357 | 358 | # Parameters 359 | if parameters: 360 | try: 361 | hostgroup_parameters = theforeman.get_hostgroup_parameters(hostgroup_id=hostgroup_id) 362 | except ForemanError as e: 363 | module.fail_json( 364 | msg='Could not get hostgroup parameters: {0}'.format(e.message)) 365 | 366 | # Delete parameters which are not defined 367 | for hostgroup_param in hostgroup_parameters: 368 | hostgroup_param_name = hostgroup_param.get('name') 369 | defined_params = [item for item in parameters if item.get( 370 | 'name') == hostgroup_param_name] 371 | if not defined_params: 372 | try: 373 | theforeman.delete_hostgroup_parameter( 374 | hostgroup_id=hostgroup_id, parameter_id=hostgroup_param.get('id')) 375 | except ForemanError as e: 376 | module.fail_json(msg='Could not delete host parameter {name}: {error}'.format( 377 | name=hostgroup_param_name)) 378 | changed = True 379 | 380 | # Create and update parameters 381 | for param in parameters: 382 | hostgroup_params = [item for item in hostgroup_parameters if item.get('name') == param.get('name')] 383 | if not hostgroup_params: 384 | try: 385 | theforeman.create_hostgroup_parameter(hostgroup_id=hostgroup_id, data=param) 386 | except ForemanError as e: 387 | module.fail_json( 388 | msg='Could not create host parameter {param_name}: {error}'.format(param_name=param.get('name'), 389 | error=e.message)) 390 | changed = True 391 | else: 392 | for hostgroup_param in hostgroup_params: 393 | hostgroup_value = hostgroup_param.get('value') 394 | param_value = param.get('value') 395 | if isinstance(param_value, list): 396 | param_value = ','.join(param_value) 397 | # Replace \n seems to be needed. Otherwise some strings are 398 | # always changed although they look equal 399 | if hostgroup_value.replace('\n', '') != param_value.replace('\n', ''): 400 | try: 401 | theforeman.update_hostgroup_parameter(hostgroup_id=hostgroup_id, 402 | parameter_id=hostgroup_param.get('id'), 403 | data=param) 404 | except ForemanError as e: 405 | module.fail_json( 406 | msg='Could not update host parameter {param_name}: {error}'.format( 407 | param_name=param.get('name'), error=e.message)) 408 | changed = True 409 | 410 | return changed, hostgroup 411 | 412 | 413 | def main(): 414 | module = AnsibleModule( 415 | argument_spec=dict( 416 | name=dict(type='str', required=True), 417 | architecture=dict(type='str', default=None), 418 | compute_profile=dict(type='str', default=None), 419 | domain=dict(type='str', default=None), 420 | environment=dict(type='str', default=None), 421 | medium=dict(type='str', default=None), 422 | operatingsystem=dict(type='str', default=None), 423 | parameters=dict(type='list', default=None), 424 | partition_table=dict(type='str', default=None), 425 | pxe_loader=dict(type='str', default=None), 426 | realm=dict(type='str', default=None), 427 | root_pass=dict(type='str', default=None, no_log=True), 428 | force_update=dict(type='bool', default=False), 429 | smart_proxy=dict(type='str', default=None), 430 | subnet=dict(type='str', default=None), 431 | state=dict(type='str', default='present', choices=['present', 'absent']), 432 | locations=dict(type='list', required=False), 433 | organizations=dict(type='list', required=False), 434 | foreman_host=dict(type='str', default='127.0.0.1'), 435 | foreman_port=dict(type='str', default='443'), 436 | foreman_user=dict(type='str', required=True), 437 | foreman_pass=dict(type='str', required=True, no_log=True), 438 | foreman_ssl=dict(type='bool', default=True) 439 | ), 440 | ) 441 | 442 | if not foremanclient_found: 443 | module.fail_json(msg='python-foreman module is required. See https://github.com/Nosmoht/python-foreman.') 444 | 445 | changed, hostgroup = ensure(module) 446 | module.exit_json(changed=changed, hostgroup=hostgroup) 447 | 448 | # import module snippets 449 | from ansible.module_utils.basic import * 450 | 451 | if __name__ == '__main__': 452 | main() 453 | -------------------------------------------------------------------------------- /foreman_image.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # -*- coding: utf-8 -*- 3 | # 4 | # Ansible module to manage Foreman operating system resources. 5 | # 6 | # This module is free software: you can redistribute it and/or modify 7 | # it under the terms of the GNU General Public License as published by 8 | # the Free Software Foundation, either version 3 of the License, or 9 | # (at your option) any later version. 10 | # 11 | # This software is distributed in the hope that it will be useful, 12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | # GNU General Public License for more details. 15 | # 16 | # You should have received a copy of the GNU General Public License 17 | # along with this software. If not, see . 18 | # 19 | 20 | DOCUMENTATION = ''' 21 | --- 22 | module: foreman_image 23 | short_description: 24 | - Manage Foreman images using Foreman API v2. 25 | description: 26 | - Create, update and and delete Foreman images using Foreman API v2 27 | options: 28 | name: 29 | description: Image name as used in Foreman 30 | required: true 31 | state: 32 | description: image state 33 | required: false 34 | default: 'present' 35 | choices: ['present', 'absent'] 36 | uuid: 37 | operatingsystem: 38 | description: Operatingsystem used on the image 39 | required: True 40 | architecture: 41 | description: Architecture the image is for 42 | required: True 43 | uuid: 44 | description: UUID of the image 45 | required: True 46 | user: 47 | description: User used to log into the image 48 | required: False 49 | default: root 50 | foreman_host: 51 | description: Hostname or IP address of Foreman system 52 | required: false 53 | default: 127.0.0.1 54 | foreman_port: 55 | description: Port of Foreman API 56 | required: false 57 | default: 443 58 | foreman_user: 59 | description: Username to be used to authenticate on Foreman 60 | required: true 61 | foreman_pass: 62 | description: Password to be used to authenticate user on Foreman 63 | required: true 64 | foreman_ssl: 65 | description: Enable SSL when connecting to Foreman API 66 | required: false 67 | default: true 68 | notes: 69 | - Requires the python-foreman package to be installed. See https://github.com/Nosmoht/python-foreman. 70 | version_added: "2.0" 71 | author: "Guido Günther " 72 | ''' 73 | 74 | EXAMPLES = ''' 75 | - name: Ensure Debian Jessie Image 76 | foreman_image: 77 | name: Debian Jessie Minimal 78 | architecture: x86_64 79 | operatingsystem: DebianJessie 80 | uuid: /path/to/image 81 | state: present 82 | foreman_host: 127.0.0.1 83 | foreman_port: 443 84 | foreman_user: admin 85 | foreman_pass: secret 86 | ''' 87 | 88 | try: 89 | from foreman.foreman import * 90 | 91 | foremanclient_found = True 92 | except ImportError: 93 | foremanclient_found = False 94 | 95 | 96 | def get_resources(resource_type, resource_func, resource_name, search_field='name'): 97 | if not resource_name: 98 | return None 99 | search_data = dict() 100 | search_data[search_field] = resource_name 101 | try: 102 | resource = resource_func(data=search_data) 103 | if not resource: 104 | module.fail_json( 105 | msg='Could not find resource type {resource_type} specified as {name}'.format( 106 | resource_type=resource_type, name=resource_name)) 107 | except ForemanError as e: 108 | module.fail_json(msg='Could not search resource type {resource_type} specified as {name}: {error}'.format( 109 | resource_type=resource_type, name=resource_name, error=e.message)) 110 | return resource 111 | 112 | 113 | def ensure(): 114 | name = module.params['name'] 115 | compute_resource_name = module.params['compute_resource'] 116 | state = module.params['state'] 117 | 118 | data = dict(name=name) 119 | 120 | try: 121 | compute_resource = theforeman.search_compute_resource(data=dict(name=compute_resource_name)) 122 | except ForemanError as e: 123 | module.fail_json(msg='Could not find compute resource {0}: {1}'.format(compute_resource_name, e.message)) 124 | 125 | if not compute_resource: 126 | module.fail_json(msg='Could not find compute resource {0}'.format(compute_resource_name)) 127 | 128 | cid = compute_resource['id'] 129 | try: 130 | images = theforeman.get_compute_resource_images(compute_resource['id']) 131 | for i in images: 132 | if i['name'] == name: 133 | image = i 134 | break 135 | else: 136 | image = None 137 | except ForemanError as e: 138 | module.fail_json(msg='Could not get images: {0}'.format(e.message)) 139 | 140 | if state == 'absent': 141 | if image: 142 | try: 143 | image = theforeman.delete_compute_resource_image(cid, image.get('id')) 144 | return True, image 145 | except ForemanError as e: 146 | module.fail_json(msg='Could not delete image: {0}'.format(e.message)) 147 | 148 | return False, image 149 | 150 | data['compute_resource_id'] = cid 151 | data['uuid'] = module.params['uuid'] 152 | data['username'] = module.params['user'] 153 | if module.params['password']: 154 | data['password'] = module.params['password'] 155 | data['architecture_id'] = get_resources(resource_type='architecture', 156 | resource_func=theforeman.search_architecture, 157 | resource_name=module.params['architecture'])['id'] 158 | data['operatingsystem_id'] = get_resources(resource_type='operatingsystem', 159 | resource_func=theforeman.search_operatingsystem, 160 | resource_name=module.params['operatingsystem'], 161 | search_field='title')['id'] 162 | 163 | if not image: 164 | try: 165 | image = theforeman.create_compute_resource_image(compute_resource_id=cid, 166 | data=data) 167 | return True, image 168 | except ForemanError as e: 169 | module.fail_json(msg='Could not create image: {0}'.format(e.message)) 170 | else: 171 | data['id'] = image['id'] 172 | 173 | if not all(data[key] == image.get(key, data[key]) for key in data.keys()): 174 | try: 175 | new_data = dict(compute_resource_id=cid, id=image['id'], image=data) 176 | image = theforeman.update_compute_resource_image(compute_resource_id=cid, 177 | data=new_data) 178 | return True, image 179 | except ForemanError as e: 180 | module.fail_json(msg='Could not update image: {0}'.format(e.message)) 181 | 182 | return False, image 183 | 184 | 185 | def main(): 186 | global module 187 | global theforeman 188 | 189 | module = AnsibleModule( 190 | argument_spec=dict( 191 | name=dict(type='str', required=True), 192 | compute_resource=dict(type='str', required=True), 193 | architecture=dict(type='str', required=True), 194 | operatingsystem=dict(operatingsystem='str', required=True), 195 | uuid=dict(type='str', required=True), 196 | user=dict(type='str', default='root'), 197 | password=dict(type='str', default=None, no_log=True), 198 | state=dict(type='str', default='present', choices=['present', 'absent']), 199 | foreman_host=dict(type='str', default='127.0.0.1'), 200 | foreman_port=dict(type='str', default='443'), 201 | foreman_user=dict(type='str', required=True), 202 | foreman_pass=dict(type='str', required=True, no_log=True), 203 | foreman_ssl=dict(type='bool', default=True) 204 | ), 205 | ) 206 | 207 | if not foremanclient_found: 208 | module.fail_json(msg='python-foreman module is required. See https://github.com/Nosmoht/python-foreman.') 209 | 210 | foreman_host = module.params['foreman_host'] 211 | foreman_port = module.params['foreman_port'] 212 | foreman_user = module.params['foreman_user'] 213 | foreman_pass = module.params['foreman_pass'] 214 | foreman_ssl = module.params['foreman_ssl'] 215 | 216 | theforeman = Foreman(hostname=foreman_host, 217 | port=foreman_port, 218 | username=foreman_user, 219 | password=foreman_pass, 220 | ssl=foreman_ssl) 221 | 222 | changed, image = ensure() 223 | module.exit_json(changed=changed, image=image) 224 | 225 | 226 | from ansible.module_utils.basic import * 227 | 228 | if __name__ == '__main__': 229 | main() 230 | -------------------------------------------------------------------------------- /foreman_ldap.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # -*- coding: utf-8 -*- 3 | # 4 | # Ansible module to manage Foreman ldap resources. 5 | # 6 | # This module is free software: you can redistribute it and/or modify 7 | # it under the terms of the GNU General Public License as published by 8 | # the Free Software Foundation, either version 3 of the License, or 9 | # (at your option) any later version. 10 | # 11 | # This software is distributed in the hope that it will be useful, 12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | # GNU General Public License for more details. 15 | # 16 | # You should have received a copy of the GNU General Public License 17 | # along with this software. If not, see . 18 | 19 | DOCUMENTATION = ''' 20 | --- 21 | module: foreman_ldap 22 | short_description: 23 | - Manage Foreman Ldap auth sources using Foreman API v2. 24 | description: 25 | - Create, opdate and and delete Foreman Ldaps using Foreman API v2 26 | options: 27 | name: 28 | description: LDAP name 29 | required: True 30 | host: 31 | description: LDAP host to use 32 | required: True 33 | port: 34 | description: LDAP port 35 | required: False 36 | default: 389 37 | tls: 38 | description: use LDAPS 39 | required: False 40 | default: False), 41 | base_dn: 42 | description: Search base 43 | required: False 44 | account: 45 | description: account for acces LDAP 46 | required: False 47 | account_password: 48 | description: password for LDAP account 49 | required: False 50 | attr_login: 51 | description: LDAP attribute used as login 52 | required: False 53 | attr_firstname: 54 | description: LDAP attribute used as firstname 55 | required: False 56 | attr_lastname 57 | description: LDAP attribute used as lastname 58 | required: False 59 | attr_mail: 60 | description: LDAP attribute used as mail 61 | required:False 62 | attr_photo: 63 | description: LDAP attribute used as photo 64 | required:False 65 | onthefly_register: 66 | description: Autocreate users authenticated by LDAP in foreman 67 | required:False 68 | usergroup_sync: 69 | description: Sync LDAP user groups automatically 70 | required:False 71 | groups_base: 72 | description: Base DN for group search used by usergroup_sync 73 | required:False 74 | server_type: 75 | description: LDAP server type, one of: posix, free_ipa, active_directory 76 | required:False 77 | ldap_filter: 78 | description: LDAP users acceptance filter 79 | required:False 80 | organizations: 81 | description: 82 | - List of organization the LDAP should be assigned to 83 | required: false 84 | locations: 85 | description: 86 | - List of locations the LDAP should be assigned to 87 | required: false 88 | state: 89 | description: Ldap state 90 | required: False 91 | default: present 92 | choices: ["present", "absent"] 93 | foreman_host: 94 | description: Hostname or IP address of Foreman system 95 | required: false 96 | default: 127.0.0.1 97 | foreman_port: 98 | description: Port of Foreman API 99 | required: false 100 | default: 443 101 | foreman_user: 102 | description: Username to be used to authenticate on Foreman 103 | required: true 104 | foreman_pass: 105 | description: Password to be used to authenticate user on Foreman 106 | required: true 107 | foreman_ssl: 108 | description: Enable SSL when connecting to Foreman API 109 | required: false 110 | default: true 111 | notes: 112 | - Requires the python-foreman package to be installed. See https://github.com/Nosmoht/python-foreman. 113 | version_added: "2.0" 114 | author: "Thomas Krahn (@nosmoht)" 115 | ''' 116 | 117 | EXAMPLES = ''' 118 | - name: A test ldap server 119 | foreman_ldap: 120 | name: Test LDAP 121 | host: 127.0.0.1 122 | state: present 123 | foreman_host: 127.0.0.1 124 | foreman_port: 443 125 | foreman_user: admin 126 | foreman_pass: secret 127 | ''' 128 | 129 | try: 130 | from foreman.foreman import * 131 | except ImportError: 132 | foremanclient_found = False 133 | else: 134 | foremanclient_found = True 135 | 136 | try: 137 | from ansible.module_utils.foreman_utils import * 138 | 139 | has_import_error = False 140 | except ImportError as e: 141 | has_import_error = True 142 | import_error_msg = str(e) 143 | 144 | 145 | def get_user_ids(module, theforeman, users): 146 | result = [] 147 | for i in range(0, len(users)): 148 | try: 149 | user = theforeman.search_user(data={'login': users[i]}) 150 | if not user: 151 | module.fail_json('Could not find user {0}'.format(users[i])) 152 | result.append(user.get('id')) 153 | except ForemanError as e: 154 | module.fail_json('Could not get user: {0}'.format(e.message)) 155 | return result 156 | 157 | 158 | def ldaps_equal(data, ldap, cmp_keys): 159 | for key in cmp_keys: 160 | if (key in data) and (data.get(key) != ldap.get(key)): 161 | return False 162 | if not organizations_equal(data, ldap): 163 | return False 164 | if not locations_equal(data, ldap): 165 | return False 166 | return True 167 | 168 | 169 | def ensure(module): 170 | name = module.params['name'] 171 | state = module.params['state'] 172 | organizations = module.params['organizations'] 173 | locations = module.params['locations'] 174 | 175 | theforeman = init_foreman_client(module) 176 | 177 | cmp_keys = ['host', 'port', 'base_dn', 'account', 'attr_login', 'attr_firstname', 178 | 'attr_lastname', 'attr_mail', 'attr_photo', 'onthefly_register', 179 | 'usergroup_sync', 'ldap_filter', 'tls', 'groups_base', 'server_type'] 180 | keys = cmp_keys + ['account_password'] 181 | 182 | data = {'name': name} 183 | 184 | try: 185 | ldap = theforeman.search_auth_source_ldap(data=data) 186 | if ldap: 187 | ldap = theforeman.get_auth_source_ldap(id=ldap.get('id')) 188 | except ForemanError as e: 189 | module.fail_json(msg='Could not get ldap: {0}'.format(e.message)) 190 | 191 | for key in keys: 192 | if module.params[key]: 193 | data[key] = module.params[key] 194 | if organizations is not None: 195 | data['organization_ids'] = get_organization_ids(module, theforeman, organizations) 196 | if locations is not None: 197 | data['location_ids'] = get_location_ids(module, theforeman, locations) 198 | 199 | if not ldap and state == 'present': 200 | try: 201 | theforeman.create_auth_source_ldap(data=data) 202 | return True 203 | except ForemanError as e: 204 | module.fail_json(msg='Could not create ldap: {0}'.format(e.message)) 205 | 206 | if ldap: 207 | if state == 'absent': 208 | try: 209 | theforeman.delete_auth_source_ldap(id=ldap.get('id')) 210 | return True 211 | except ForemanError as e: 212 | module.fail_json('Could not delete ldap: {0}'.format(e.message)) 213 | 214 | if not ldaps_equal(data, ldap, cmp_keys): 215 | try: 216 | ldap = theforeman.update_auth_source_ldap(id=ldap.get('id'), data=data) 217 | return True, ldap 218 | except ForemanError as e: 219 | module.fail_json(msg='Could not update hostgroup: {0}'.format(e.message)) 220 | return False 221 | 222 | 223 | def main(): 224 | module = AnsibleModule( 225 | argument_spec=dict( 226 | name=dict(type='str', required=True), 227 | state=dict(type='str', default='present', choices=['present', 'absent']), 228 | host=dict(type='str', required=True), 229 | port=dict(type='int', required=False, default=389), 230 | tls=dict(type='bool', required=False, default=False), 231 | base_dn=dict(type='str', required=False), 232 | account=dict(type='str', required=False), 233 | account_password=dict(type='str', required=False, no_log=True), 234 | attr_login=dict(type='str', required=False), 235 | attr_firstname=dict(type='str', required=False), 236 | attr_lastname=dict(type='str', required=False), 237 | attr_mail=dict(type='str', required=False), 238 | attr_photo=dict(type='str', required=False), 239 | onthefly_register=dict(type='bool', required=False), 240 | usergroup_sync=dict(type='bool', required=False), 241 | groups_base=dict(type='str', required=False), 242 | server_type=dict(type='str', required=False, choices=['posix', 'free_ipa', 'active_directory']), 243 | ldap_filter=dict(type='str', required=False), 244 | organizations=dict(type='list', required=False), 245 | locations=dict(type='list', required=False), 246 | foreman_host=dict(type='str', default='127.0.0.1'), 247 | foreman_port=dict(type='str', default='443'), 248 | foreman_user=dict(type='str', required=True), 249 | foreman_pass=dict(type='str', required=True, no_log=True), 250 | foreman_ssl=dict(type='bool', default=True) 251 | ), 252 | ) 253 | 254 | if not foremanclient_found: 255 | module.fail_json(msg='python-foreman module is required. See https://github.com/Nosmoht/python-foreman.') 256 | if has_import_error: 257 | module.fail_json(msg=import_error_msg) 258 | 259 | changed = ensure(module) 260 | module.exit_json(changed=changed, name=module.params['name']) 261 | 262 | 263 | from ansible.module_utils.basic import * 264 | 265 | if __name__ == '__main__': 266 | main() 267 | -------------------------------------------------------------------------------- /foreman_location.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # -*- coding: utf-8 -*- 3 | # 4 | # Ansible module to manage Foreman location resources. 5 | # 6 | # This module is free software: you can redistribute it and/or modify 7 | # it under the terms of the GNU General Public License as published by 8 | # the Free Software Foundation, either version 3 of the License, or 9 | # (at your option) any later version. 10 | # 11 | # This software is distributed in the hope that it will be useful, 12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | # GNU General Public License for more details. 15 | # 16 | # You should have received a copy of the GNU General Public License 17 | # along with this software. If not, see . 18 | 19 | DOCUMENTATION = ''' 20 | --- 21 | module: foreman_location 22 | short_description: 23 | - Manage Foreman Location using Foreman API v2. This module requires Katello to be installed in addition to Foreman. 24 | description: 25 | - Create, opdate and and delete Foreman Locations using Foreman API v2 26 | options: 27 | name: 28 | description: Location name 29 | required: True 30 | state: 31 | description: Location state 32 | required: False 33 | default: present 34 | choices: ["present", "absent"] 35 | users: 36 | description: List of usernames assigned to the location 37 | required: False 38 | default: None 39 | foreman_host: 40 | description: Hostname or IP address of Foreman system 41 | required: false 42 | default: 127.0.0.1 43 | foreman_port: 44 | description: Port of Foreman API 45 | required: false 46 | default: 443 47 | foreman_user: 48 | description: Username to be used to authenticate on Foreman 49 | required: true 50 | foreman_pass: 51 | description: Password to be used to authenticate user on Foreman 52 | required: true 53 | foreman_ssl: 54 | description: Enable SSL when connecting to Foreman API 55 | required: false 56 | default: true 57 | notes: 58 | - Requires the python-foreman package to be installed. See https://github.com/Nosmoht/python-foreman. 59 | version_added: "2.0" 60 | author: "Thomas Krahn (@nosmoht)" 61 | ''' 62 | 63 | EXAMPLES = ''' 64 | - name: Ensure New York Datacenter 65 | foreman_location: 66 | name: MY-DC 67 | state: present 68 | users: 69 | - pinky 70 | - brain 71 | foreman_host: 127.0.0.1 72 | foreman_port: 443 73 | foreman_user: admin 74 | foreman_pass: secret 75 | ''' 76 | 77 | try: 78 | from foreman.foreman import * 79 | except ImportError: 80 | foremanclient_found = False 81 | else: 82 | foremanclient_found = True 83 | 84 | 85 | def get_user_ids(module, theforeman, users): 86 | result = [] 87 | for i in range(0, len(users)): 88 | try: 89 | user = theforeman.search_user(data={'login': users[i]}) 90 | if not user: 91 | module.fail_json('Could not find user {0}'.format(users[i])) 92 | result.append(user.get('id')) 93 | except ForemanError as e: 94 | module.fail_json('Could not get user: {0}'.format(e.message)) 95 | return result 96 | 97 | 98 | def ensure(module): 99 | name = module.params['name'] 100 | state = module.params['state'] 101 | users = module.params['users'] 102 | 103 | foreman_host = module.params['foreman_host'] 104 | foreman_port = module.params['foreman_port'] 105 | foreman_user = module.params['foreman_user'] 106 | foreman_pass = module.params['foreman_pass'] 107 | foreman_ssl = module.params['foreman_ssl'] 108 | 109 | theforeman = Foreman(hostname=foreman_host, 110 | port=foreman_port, 111 | username=foreman_user, 112 | password=foreman_pass, 113 | ssl=foreman_ssl) 114 | 115 | data = {'name': name} 116 | 117 | try: 118 | location = theforeman.search_location(data=data) 119 | except ForemanError as e: 120 | module.fail_json(msg='Could not get location: {0}'.format(e.message)) 121 | 122 | if users: 123 | data['user_ids'] = get_user_ids(module, theforeman, users) 124 | 125 | if not location and state == 'present': 126 | try: 127 | theforeman.create_location(data=data) 128 | return True 129 | except ForemanError as e: 130 | module.fail_json(msg='Could not create location: {0}'.format(e.message)) 131 | 132 | if location: 133 | if state == 'absent': 134 | try: 135 | theforeman.delete_location(id=location.get('id')) 136 | return True 137 | except ForemanError as e: 138 | module.fail_json('Could not delete location: {0}'.format(e.message)) 139 | 140 | return False 141 | 142 | 143 | def main(): 144 | module = AnsibleModule( 145 | argument_spec=dict( 146 | name=dict(type='str', required=True), 147 | state=dict(type='str', default='present', choices=['present', 'absent']), 148 | users=dict(type='list', required=False), 149 | foreman_host=dict(type='str', default='127.0.0.1'), 150 | foreman_port=dict(type='str', default='443'), 151 | foreman_user=dict(type='str', required=True), 152 | foreman_pass=dict(type='str', required=True, no_log=True), 153 | foreman_ssl=dict(type='bool', default=True) 154 | ), 155 | ) 156 | 157 | if not foremanclient_found: 158 | module.fail_json(msg='python-foreman module is required. See https://github.com/Nosmoht/python-foreman.') 159 | 160 | changed = ensure(module) 161 | module.exit_json(changed=changed, name=module.params['name']) 162 | 163 | 164 | from ansible.module_utils.basic import * 165 | 166 | if __name__ == '__main__': 167 | main() 168 | -------------------------------------------------------------------------------- /foreman_medium.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # -*- coding: utf-8 -*- 3 | # 4 | # Ansible module to manage Foreman medium resources. 5 | # 6 | # This module is free software: you can redistribute it and/or modify 7 | # it under the terms of the GNU General Public License as published by 8 | # the Free Software Foundation, either version 3 of the License, or 9 | # (at your option) any later version. 10 | # 11 | # This software is distributed in the hope that it will be useful, 12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | # GNU General Public License for more details. 15 | # 16 | # You should have received a copy of the GNU General Public License 17 | # along with this software. If not, see . 18 | 19 | DOCUMENTATION = ''' 20 | --- 21 | module: foreman_medium 22 | short_description: Manage Foreman media using Foreman API v2 23 | description: 24 | - Create, update and delete Foreman media using Foreman API v2 25 | options: 26 | name: 27 | description: 28 | - Medium name, a combination of name '*' and state 'absent' can be used to clean up, by deleting all present media 29 | required: true 30 | path: 31 | description: 32 | - The path to the medium, can be a URL or a valid NFS server (exclusive of the architecture). 33 | required: true 34 | os_family: 35 | description: 36 | - Operating system family 37 | required: false 38 | state: 39 | description: 40 | - Medium state, , a combination of name '*' and state 'absent' can be used to clean up, by deleting all present media 41 | required: false 42 | default: 'present' 43 | choices: ['present', 'absent'] 44 | locations: 45 | description: 46 | - List of locations the medium should be assigned to 47 | required: false 48 | organizations: 49 | description: 50 | - List of organization the medium should be assigned to 51 | required: false 52 | foreman_host: 53 | description: 54 | - Hostname or IP address of Foreman system 55 | required: false 56 | default: 127.0.0.1 57 | foreman_port: 58 | description: 59 | - Port of Foreman API 60 | required: false 61 | default: 443 62 | foreman_user: 63 | description: 64 | - Username to be used to authenticate on Foreman 65 | required: true 66 | foreman_pass: 67 | description: 68 | - Password to be used to authenticate user on Foreman 69 | required: true 70 | foreman_ssl: 71 | description: Enable SSL when connecting to Foreman API 72 | required: false 73 | default: true 74 | notes: 75 | - Requires the python-foreman package to be installed. See https://github.com/Nosmoht/python-foreman. 76 | version_added: "2.0" 77 | author: "Thomas Krahn (@nosmoht)" 78 | ''' 79 | 80 | EXAMPLES = ''' 81 | - name: Medium 82 | foreman_medium: 83 | name: CentOS mirror 84 | path: http://mirror.centos.org/centos/$version/os/$arch 85 | os_family: Redhat 86 | locations: 87 | - Munich 88 | organizations: 89 | - Development 90 | - DevOps 91 | state: present 92 | foreman_user: admin 93 | foreman_pass: secret 94 | foreman_host: foreman.example.com 95 | foreman_port: 443 96 | 97 | - name: delete all Media 98 | foreman_medium: 99 | name: * 100 | state: absent 101 | ... 102 | ''' 103 | 104 | try: 105 | from foreman.foreman import * 106 | 107 | foremanclient_found = True 108 | except ImportError: 109 | foremanclient_found = False 110 | 111 | try: 112 | from ansible.module_utils.foreman_utils import * 113 | 114 | has_import_error = False 115 | except ImportError as e: 116 | has_import_error = True 117 | import_error_msg = str(e) 118 | 119 | 120 | def medium_equal(data, medium, module): 121 | comparable_keys = set(data.keys()).intersection(set( 122 | ['path', 'os_family'])) 123 | if not all(data.get(key, None) == medium.get(key, None) for key in comparable_keys): 124 | return False 125 | if not organizations_equal(data, medium): 126 | return False 127 | if not locations_equal(data, medium): 128 | return False 129 | return True 130 | 131 | 132 | def ensure(module): 133 | name = module.params['name'] 134 | path = module.params['path'] 135 | state = module.params['state'] 136 | os_family = module.params['os_family'] 137 | organizations = module.params['organizations'] 138 | locations = module.params['locations'] 139 | 140 | foreman_host = module.params['foreman_host'] 141 | foreman_port = module.params['foreman_port'] 142 | foreman_user = module.params['foreman_user'] 143 | foreman_pass = module.params['foreman_pass'] 144 | foreman_ssl = module.params['foreman_ssl'] 145 | 146 | theforeman = Foreman(hostname=foreman_host, 147 | port=foreman_port, 148 | username=foreman_user, 149 | password=foreman_pass, 150 | ssl=foreman_ssl) 151 | 152 | data = {'name': name} 153 | 154 | if name == '*' and state == 'absent': 155 | try: 156 | all_media_list = theforeman.get_resources(resource_type=MEDIA) 157 | for element in all_media_list: 158 | theforeman.delete_medium(id=element.get('id')) 159 | return True, all_media_list 160 | except ForemanError as e: 161 | module.fail_json(msg='Error in deleting all existing media: {0}'.format(e.message)) 162 | 163 | try: 164 | medium = theforeman.search_medium(data=data) 165 | if medium: 166 | medium = theforeman.get_medium(id=medium.get('id')) 167 | except ForemanError as e: 168 | module.fail_json(msg='Could not get medium: {0}'.format(e.message)) 169 | 170 | if path: 171 | data['path'] = path 172 | if os_family: 173 | data['os_family'] = os_family 174 | if organizations is not None: 175 | data['organization_ids'] = get_organization_ids(module, theforeman, organizations) 176 | if locations is not None: 177 | data['location_ids'] = get_location_ids(module, theforeman, locations) 178 | 179 | if not medium and state == 'present': 180 | try: 181 | medium = theforeman.create_medium(data=data) 182 | return True, medium 183 | except ForemanError as e: 184 | module.fail_json(msg='Could not create medium: {0}'.format(e.message)) 185 | 186 | if medium: 187 | if state == 'absent': 188 | try: 189 | medium = theforeman.delete_medium(id=medium.get('id')) 190 | return True, medium 191 | except ForemanError as e: 192 | module.fail_json('Could not delete medium: {0}'.format(e.message)) 193 | if not medium_equal(data, medium, module): 194 | try: 195 | medium = theforeman.update_medium(id=medium.get('id'), data=data) 196 | return True, medium 197 | except ForemanError as e: 198 | module.fail_json(msg='Could not update medium: {0}'.format(e.message)) 199 | 200 | return False, medium 201 | 202 | 203 | def main(): 204 | module = AnsibleModule( 205 | argument_spec=dict( 206 | name=dict(type='str', required=True), 207 | path=dict(type='str', required=False), 208 | os_family=dict(type='str', required=False), 209 | organizations=dict(type='list', required=False), 210 | locations=dict(type='list', required=False), 211 | state=dict(type='str', default='present', choices=['present', 'absent']), 212 | foreman_host=dict(type='str', default='127.0.0.1'), 213 | foreman_port=dict(type='str', default='443'), 214 | foreman_user=dict(type='str', required=True), 215 | foreman_pass=dict(type='str', required=True, no_log=True), 216 | foreman_ssl=dict(type='bool', default=True) 217 | ), 218 | ) 219 | 220 | if not foremanclient_found: 221 | module.fail_json(msg='python-foreman module is required. See https://github.com/Nosmoht/python-foreman.') 222 | if has_import_error: 223 | module.fail_json(msg=import_error_msg) 224 | 225 | changed, medium = ensure(module) 226 | module.exit_json(changed=changed, medium=medium) 227 | 228 | 229 | from ansible.module_utils.basic import * 230 | 231 | if __name__ == '__main__': 232 | main() 233 | -------------------------------------------------------------------------------- /foreman_operatingsystem.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # -*- coding: utf-8 -*- 3 | # 4 | # Ansible module to manage Foreman operating system resources. 5 | # 6 | # This module is free software: you can redistribute it and/or modify 7 | # it under the terms of the GNU General Public License as published by 8 | # the Free Software Foundation, either version 3 of the License, or 9 | # (at your option) any later version. 10 | # 11 | # This software is distributed in the hope that it will be useful, 12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | # GNU General Public License for more details. 15 | # 16 | # You should have received a copy of the GNU General Public License 17 | # along with this software. If not, see . 18 | 19 | DOCUMENTATION = ''' 20 | --- 21 | module: foreman_operatingsystem 22 | short_description: 23 | - Manage Foreman Operatingsystems using Foreman API v2. 24 | description: 25 | - Create, update and and delete Foreman Operatingsystems using Foreman API v2 26 | options: 27 | description: 28 | description: OS description 29 | required: false 30 | default: None 31 | name: 32 | description: OS name 33 | required: true 34 | major: 35 | description: OS major version 36 | required: true 37 | minor: 38 | description: OS minor version 39 | required: false 40 | default: None 41 | family: 42 | description: Family 43 | required: false 44 | default: None 45 | release_name: 46 | description: Release name 47 | required: false 48 | default: None 49 | state: 50 | description: OS state 51 | required: false 52 | default: 'present' 53 | choices: ['present', 'absent'] 54 | foreman_host: 55 | description: Hostname or IP address of Foreman system 56 | required: false 57 | default: 127.0.0.1 58 | foreman_port: 59 | description: Port of Foreman API 60 | required: false 61 | default: 443 62 | foreman_user: 63 | description: Username to be used to authenticate on Foreman 64 | required: true 65 | foreman_pass: 66 | description: Password to be used to authenticate user on Foreman 67 | required: true 68 | foreman_ssl: 69 | description: Enable SSL when connecting to Foreman API 70 | required: false 71 | default: true 72 | notes: 73 | - Requires the python-foreman package to be installed. See https://github.com/Nosmoht/python-foreman. 74 | version_added: "2.0" 75 | author: "Thomas Krahn (@nosmoht)" 76 | ''' 77 | 78 | EXAMPLES = ''' 79 | - name: Ensure CoreOS 607.0.0 80 | foreman_operatingsystem: 81 | name: CoreOS 607.0.0 82 | architectures: 83 | - x86_64 84 | description: CoreOS Current stable 85 | media: 86 | - CoreOS mirror 87 | major: 607 88 | minor: 0.0 89 | ptables: 90 | - CoreOS default fake 91 | state: present 92 | foreman_host: 127.0.0.1 93 | foreman_port: 443 94 | foreman_user: admin 95 | foreman_pass: secret 96 | ''' 97 | 98 | try: 99 | from foreman.foreman import * 100 | 101 | foremanclient_found = True 102 | except ImportError: 103 | foremanclient_found = False 104 | 105 | try: 106 | from ansible.module_utils.foreman_utils import * 107 | 108 | has_import_error = False 109 | except ImportError as e: 110 | has_import_error = True 111 | import_error_msg = str(e) 112 | 113 | 114 | def oses_equal(data, os, comparable_keys, comparable_arrays=[]): 115 | if not all(data.get(key, None) == os.get(key, None) for key in comparable_keys): 116 | return False 117 | if not all(equal_dict_lists(l1=data.get(key, []), l2=os.get(key, [])) for key in comparable_arrays): 118 | return False 119 | if not organizations_equal(data, os): 120 | return False 121 | if not locations_equal(data, os): 122 | return False 123 | return True 124 | 125 | 126 | def get_resources(resource_type, resource_specs, theforeman, module): 127 | result = [] 128 | for item in resource_specs: 129 | search_data = dict() 130 | if isinstance(item, dict): 131 | for key in item: 132 | search_data[key] = item[key] 133 | else: 134 | search_data['name'] = item 135 | try: 136 | resource = theforeman.search_resource(resource_type=resource_type, data=search_data) 137 | if not resource: 138 | module.fail_json( 139 | msg='Could not find resource type {resource_type} defined as {spec}'.format( 140 | resource_type=resource_type, 141 | spec=item)) 142 | result.append(resource) 143 | except ForemanError as e: 144 | module.fail_json(msg='Could not search resource type {resource_type} defined as {spec}: {error}'.format( 145 | resource_type=resource_type, spec=item, error=e.message)) 146 | return result 147 | 148 | 149 | def ensure(module): 150 | comparable_keys = ['description', 'family', 'major', 'minor', 'release_name'] 151 | comparable_arrays = ['architectures'] 152 | name = module.params['name'] 153 | state = module.params['state'] 154 | 155 | theforeman = init_foreman_client(module) 156 | 157 | data = dict(name=name) 158 | data['major'] = module.params['major'] 159 | if module.params['minor']: 160 | data['minor'] = module.params['minor'] 161 | 162 | try: 163 | os = theforeman.search_operatingsystem(data=data) 164 | if os: 165 | os = theforeman.get_operatingsystem(id=os.get('id')) 166 | except ForemanError as e: 167 | module.fail_json(msg='Could not get operatingsystem: {0}'.format(e.message)) 168 | 169 | if state == 'absent': 170 | if os: 171 | try: 172 | os = theforeman.delete_operatingsystem(id=os.get('id')) 173 | return True, os 174 | except ForemanError as e: 175 | module.fail_json(msg='Could not delete operatingsystem: {0}'.format(e.message)) 176 | 177 | return False, os 178 | 179 | data['architectures'] = get_resources(resource_type='architectures', resource_specs=module.params['architectures'], 180 | theforeman=theforeman, module=module) 181 | data['description'] = module.params['description'] 182 | data['family'] = module.params['family'] 183 | data['minor'] = module.params['minor'] 184 | if module.params['media']: 185 | data['media'] = get_resources(resource_type='media', resource_specs=module.params['media'], 186 | theforeman=theforeman, module=module) 187 | comparable_arrays.append('media') 188 | if module.params['ptables']: 189 | data['ptables'] = get_resources(resource_type='ptables', resource_specs=module.params['ptables'], 190 | theforeman=theforeman, module=module) 191 | comparable_arrays.append('ptables') 192 | data['release_name'] = module.params['release_name'] 193 | 194 | if not os: 195 | try: 196 | os = theforeman.create_operatingsystem(data=data) 197 | return True, os 198 | except ForemanError as e: 199 | module.fail_json(msg='Could not create operatingsystem: {0}'.format(e.message)) 200 | 201 | if not oses_equal(data, os, comparable_keys, comparable_arrays): 202 | try: 203 | os = theforeman.update_operatingsystem(id=os.get('id'), data=data) 204 | return True, os 205 | except ForemanError as e: 206 | module.fail_json(msg='Could not update operatingsystem: {0}'.format(e.message)) 207 | 208 | return False, os 209 | 210 | 211 | def main(): 212 | module = AnsibleModule( 213 | argument_spec=dict( 214 | architectures=dict(type='list', required=False), 215 | description=dict(type='str', required=False), 216 | family=dict(type='str', required=False), 217 | major=dict(type='str', required=False), 218 | media=dict(type='list', required=False), 219 | minor=dict(type='str', required=False), 220 | name=dict(type='str', required=True), 221 | ptables=dict(type='list', required=False), 222 | release_name=dict(type='str', required=False), 223 | state=dict(type='str', default='present', choices=['present', 'absent']), 224 | foreman_host=dict(type='str', default='127.0.0.1'), 225 | foreman_port=dict(type='str', default='443'), 226 | foreman_user=dict(type='str', required=True), 227 | foreman_pass=dict(type='str', required=True, no_log=True), 228 | foreman_ssl=dict(type='bool', default=True) 229 | ), 230 | ) 231 | 232 | if not foremanclient_found: 233 | module.fail_json(msg='python-foreman module is required. See https://github.com/Nosmoht/python-foreman.') 234 | 235 | changed, os = ensure(module) 236 | module.exit_json(changed=changed, operatingsystem=os) 237 | 238 | 239 | from ansible.module_utils.basic import * 240 | 241 | if __name__ == '__main__': 242 | main() 243 | -------------------------------------------------------------------------------- /foreman_organization.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # -*- coding: utf-8 -*- 3 | # 4 | # Ansible module to manage Foreman organization resources. 5 | # 6 | # This module is free software: you can redistribute it and/or modify 7 | # it under the terms of the GNU General Public License as published by 8 | # the Free Software Foundation, either version 3 of the License, or 9 | # (at your option) any later version. 10 | # 11 | # This software is distributed in the hope that it will be useful, 12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | # GNU General Public License for more details. 15 | # 16 | # You should have received a copy of the GNU General Public License 17 | # along with this software. If not, see . 18 | 19 | DOCUMENTATION = ''' 20 | --- 21 | module: foreman_organization 22 | short_description: Manage Foreman organization resources using Foreman API v2 23 | description: 24 | - Create and delete Foreman organization resources using Foreman API v2 25 | options: 26 | name: 27 | description: Organization name 28 | required: true 29 | state: 30 | description: Organization state 31 | required: false 32 | default: present 33 | choices: ["present", "absent"] 34 | foreman_host: 35 | description: Hostname or IP address of Foreman system 36 | required: false 37 | default: 127.0.0.1 38 | foreman_port: 39 | description: Port of Foreman API 40 | required: false 41 | default: 443 42 | foreman_user: 43 | description: Username to be used to authenticate on Foreman 44 | required: true 45 | foreman_pass: 46 | description: Password to be used to authenticate user on Foreman 47 | required: true 48 | foreman_ssl: 49 | description: Enable SSL when connecting to Foreman API 50 | required: false 51 | default: true 52 | notes: 53 | - Requires the python-foreman package to be installed. See https://github.com/Nosmoht/python-foreman. 54 | version_added: "2.0" 55 | author: "Thomas Krahn (@nosmoht)" 56 | ''' 57 | 58 | try: 59 | from foreman.foreman import * 60 | except ImportError: 61 | foremanclient_found = False 62 | else: 63 | foremanclient_found = True 64 | 65 | 66 | def ensure(module): 67 | name = module.params['name'] 68 | state = module.params['state'] 69 | 70 | foreman_host = module.params['foreman_host'] 71 | foreman_port = module.params['foreman_port'] 72 | foreman_user = module.params['foreman_user'] 73 | foreman_pass = module.params['foreman_pass'] 74 | foreman_ssl = module.params['foreman_ssl'] 75 | 76 | theforeman = Foreman(hostname=foreman_host, 77 | port=foreman_port, 78 | username=foreman_user, 79 | password=foreman_pass, 80 | ssl=foreman_ssl) 81 | 82 | data = {'name': name} 83 | 84 | try: 85 | organization = theforeman.search_organization(data=data) 86 | except ForemanError as e: 87 | module.fail_json(msg='Could not get organization: {0}'.format(e.message)) 88 | 89 | if not organization and state == 'present': 90 | try: 91 | theforeman.create_organization(data=data) 92 | return True 93 | except ForemanError as e: 94 | module.fail_json(msg='Could not create organization: {0}'.format(e.message)) 95 | 96 | if organization and state == 'absent': 97 | try: 98 | theforeman.delete_organization(id=organization.get('id')) 99 | return True 100 | except ForemanError as e: 101 | module.fail_json('Could not delete organization: {0}'.format(e.message)) 102 | 103 | return False 104 | 105 | 106 | def main(): 107 | module = AnsibleModule( 108 | argument_spec=dict( 109 | name=dict(type='str', required=True), 110 | state=dict(type='str', default='present', choices=['present', 'absent']), 111 | foreman_host=dict(type='str', default='127.0.0.1'), 112 | foreman_port=dict(type='str', default='443'), 113 | foreman_user=dict(type='str', required=True), 114 | foreman_pass=dict(type='str', required=True, no_log=True), 115 | foreman_ssl=dict(type='bool', default=True) 116 | ), 117 | ) 118 | 119 | if not foremanclient_found: 120 | module.fail_json(msg='python-foreman module is required. See https://github.com/Nosmoht/python-foreman.') 121 | 122 | changed = ensure(module) 123 | module.exit_json(changed=changed, name=module.params['name']) 124 | 125 | 126 | from ansible.module_utils.basic import * 127 | 128 | if __name__ == '__main__': 129 | main() 130 | -------------------------------------------------------------------------------- /foreman_os_default_template.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # -*- coding: utf-8 -*- 3 | # 4 | # Ansible module to manage Foreman operating system default template resources. 5 | # 6 | # This module is free software: you can redistribute it and/or modify 7 | # it under the terms of the GNU General Public License as published by 8 | # the Free Software Foundation, either version 3 of the License, or 9 | # (at your option) any later version. 10 | # 11 | # This software is distributed in the hope that it will be useful, 12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | # GNU General Public License for more details. 15 | # 16 | # You should have received a copy of the GNU General Public License 17 | # along with this software. If not, see . 18 | 19 | DOCUMENTATION = ''' 20 | --- 21 | module: foreman_os_default_template 22 | short_description: Manage Foreman Operatingsystem default templates using Foreman API v2 23 | description: 24 | - Create,update and delete Foreman Operatingsystem default templates using Foreman API v2 25 | options: 26 | operatingsystem: 27 | description: Operatingsystem name 28 | required: true 29 | config_template: 30 | description: Config Template name 31 | required: true 32 | template_kind: 33 | description: Template kind 34 | required: true 35 | state: 36 | description: OS Default template state 37 | required: false 38 | default: 'present' 39 | choices: ['present', 'absent'] 40 | foreman_host: 41 | description: Hostname or IP address of Foreman system 42 | required: false 43 | default: 127.0.0.1 44 | foreman_port: 45 | description: Port of Foreman API 46 | required: false 47 | default: 443 48 | foreman_user: 49 | description: Username to be used to authenticate on Foreman 50 | required: true 51 | foreman_pass: 52 | description: Password to be used to authenticate user on Foreman 53 | required: true 54 | foreman_ssl: 55 | description: Enable SSL when connecting to Foreman API 56 | required: false 57 | default: true 58 | notes: 59 | - Requires the python-foreman package to be installed. See https://github.com/Nosmoht/python-foreman. 60 | version_added: "2.0" 61 | author: "Thomas Krahn (@nosmoht)" 62 | ''' 63 | 64 | EXAMPLES = ''' 65 | - name: Ensure OS Default Template 66 | foreman_os_default_template: 67 | operatingsystem: CoreOS 68 | config_template: CoreOS PXELinux 69 | template_kind: PXELinux 70 | state: present 71 | foreman_user: admin 72 | foreman_pass: secret 73 | foreman_host: foreman.example.com 74 | foreman_port: 443 75 | ''' 76 | 77 | try: 78 | from foreman.foreman import * 79 | except ImportError: 80 | foremanclient_found = False 81 | else: 82 | foremanclient_found = True 83 | 84 | 85 | def ensure(): 86 | os_name = module.params['operatingsystem'] 87 | config_template_name = module.params['config_template'] 88 | template_kind_name = module.params['template_kind'] 89 | state = module.params['state'] 90 | 91 | try: 92 | os = theforeman.search_operatingsystem(data=dict(name=os_name)) 93 | except ForemanError as e: 94 | module.fail_json(msg='Could not search operatingsystem: {0}'.format(e.message)) 95 | 96 | if not os: 97 | module.fail_json(msg='Operatingsystem {os_name} not found'.format(os_name=os_name)) 98 | 99 | try: 100 | config_templates = theforeman.get_config_templates() 101 | except: 102 | module.fail_json(msg='Could not get config templates: {0}'.format(e.message)) 103 | 104 | config_template = None 105 | for item in config_templates: 106 | if item.get('name') == config_template_name and item.get('template_kind_name') == template_kind_name: 107 | config_template = item 108 | break 109 | 110 | if not config_template: 111 | module.fail_json(msg='Could not find config template {config_template} of kind {template_kind}'.format( 112 | config_template_name=config_template_name, template_kind=template_kind_name)) 113 | 114 | try: 115 | os_default_templates = theforeman.get_operatingsystem_default_templates(id=os.get('id')) 116 | except ForemanError as e: 117 | module.fail_json(msg='Could not get operatingsystem default templates: {0}'.format(e.message)) 118 | 119 | os_default_template = None 120 | for item in os_default_templates: 121 | if (item.get('config_template_id') == config_template.get('id')) and ( 122 | item.get('template_kind_id') == config_template.get('template_kind_id')): 123 | os_default_template = item 124 | break 125 | 126 | if state == 'absent': 127 | if os_default_template: 128 | try: 129 | os_default_template = theforeman.delete_operatingsystem_default_template(id=os.get('id'), 130 | template_id=os_default_template.get( 131 | 'id')) 132 | except: 133 | module.fail_json(msg='Could not delete operatingsystem default template: {0}'.format(e.message)) 134 | return True, os_default_template 135 | return False, os_default_template 136 | 137 | if not os_default_template: 138 | try: 139 | os_default_template = theforeman.create_operatingsystem_default_template(id=os.get('id'), data=dict( 140 | config_template_id=config_template.get('id'), template_kind_id=config_template.get('template_kind_id'))) 141 | except ForemanError as e: 142 | module.fail_json(msg='Could not create operatingsystem default template: {0}'.format(e.message)) 143 | return True, os_default_template 144 | 145 | return False, os_default_template 146 | 147 | 148 | def main(): 149 | global module 150 | global theforeman 151 | 152 | module = AnsibleModule( 153 | argument_spec=dict( 154 | operatingsystem=dict(type='str', required=True), 155 | config_template=dict(type='str', required=True), 156 | template_kind=dict(type='str', required=True), 157 | state=dict(type='str', default='present', choices=['present', 'absent']), 158 | foreman_host=dict(type='str', default='127.0.0.1'), 159 | foreman_port=dict(type='str', default='443'), 160 | foreman_user=dict(type='str', required=True), 161 | foreman_pass=dict(type='str', required=True, no_log=True), 162 | foreman_ssl=dict(type='bool', default=True) 163 | ), 164 | ) 165 | 166 | if not foremanclient_found: 167 | module.fail_json(msg='python-foreman module is required. See https://github.com/Nosmoht/python-foreman.') 168 | 169 | foreman_host = module.params['foreman_host'] 170 | foreman_port = module.params['foreman_port'] 171 | foreman_user = module.params['foreman_user'] 172 | foreman_pass = module.params['foreman_pass'] 173 | foreman_ssl = module.params['foreman_ssl'] 174 | 175 | theforeman = Foreman(hostname=foreman_host, 176 | port=foreman_port, 177 | username=foreman_user, 178 | password=foreman_pass, 179 | ssl=foreman_ssl) 180 | 181 | changed, os_default_template = ensure() 182 | module.exit_json(changed=changed, os_default_template=os_default_template) 183 | 184 | 185 | from ansible.module_utils.basic import * 186 | 187 | if __name__ == '__main__': 188 | main() 189 | -------------------------------------------------------------------------------- /foreman_ptable.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # -*- coding: utf-8 -*- 3 | # 4 | # Ansible module to manage Foreman partition table resources. 5 | # 6 | # This module is free software: you can redistribute it and/or modify 7 | # it under the terms of the GNU General Public License as published by 8 | # the Free Software Foundation, either version 3 of the License, or 9 | # (at your option) any later version. 10 | # 11 | # This software is distributed in the hope that it will be useful, 12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | # GNU General Public License for more details. 15 | # 16 | # You should have received a copy of the GNU General Public License 17 | # along with this software. If not, see . 18 | 19 | DOCUMENTATION = ''' 20 | --- 21 | module: foreman_ptable 22 | short_description: Manage Foreman Partition Tables using Foreman API v2 23 | description: 24 | - Create, update and delete Foreman Partition Tables using Foreman API v2 25 | options: 26 | name: 27 | description: 28 | - Partition Table name 29 | required: true 30 | layout: 31 | description: 32 | - Partition Table layout 33 | required: false 34 | os_family: 35 | description: 36 | - OS family 37 | required: false 38 | operatingsystems: 39 | description: 40 | - List of Operating systems 41 | required: False 42 | default: None 43 | organizations: 44 | description: 45 | - List of organization the ptable should be assigned to 46 | required: false 47 | locations: 48 | description: 49 | - List of locations the ptable should be assigned to 50 | required: false 51 | state: 52 | description: 53 | - Partition Table state 54 | required: false 55 | default: 'present' 56 | choices: ['present', 'absent'] 57 | foreman_host: 58 | description: 59 | - Hostname or IP address of Foreman system 60 | required: false 61 | default: 127.0.0.1 62 | foreman_port: 63 | description: 64 | - Port of Foreman API 65 | required: false 66 | default: 443 67 | foreman_user: 68 | description: 69 | - Username to be used to authenticate on Foreman 70 | required: true 71 | foreman_pass: 72 | description: 73 | - Password to be used to authenticate user on Foreman 74 | required: true 75 | foreman_ssl: 76 | description: Enable SSL when connecting to Foreman API 77 | required: false 78 | default: true 79 | notes: 80 | - Requires the python-foreman package to be installed. See https://github.com/Nosmoht/python-foreman. 81 | version_added: "2.0" 82 | author: "Thomas Krahn (@nosmoht)" 83 | ''' 84 | 85 | EXAMPLES = ''' 86 | - name: Ensure Partition Table 87 | foreman_ptable: 88 | name: FreeBSD 89 | layout: 'some layout' 90 | state: present 91 | foreman_user: admin 92 | foreman_pass: secret 93 | foreman_host: foreman.example.com 94 | foreman_port: 443 95 | ''' 96 | 97 | try: 98 | from foreman.foreman import * 99 | except ImportError: 100 | foremanclient_found = False 101 | else: 102 | foremanclient_found = True 103 | 104 | try: 105 | from ansible.module_utils.foreman_utils import * 106 | 107 | has_import_error = False 108 | except ImportError as e: 109 | has_import_error = True 110 | import_error_msg = str(e) 111 | 112 | def ptables_equal(data, ptable): 113 | comparable_keys = set(data.keys()).intersection(set( 114 | ['layout', 'os_family'])) 115 | if not all(data.get(key, None) == ptable.get(key, None) for key in comparable_keys): 116 | return False 117 | if not operatingsystems_equal(data, ptable): 118 | return False 119 | if not organizations_equal(data, ptable): 120 | return False 121 | if not locations_equal(data, ptable): 122 | return False 123 | return True 124 | 125 | def ensure(): 126 | name = module.params['name'] 127 | layout = module.params['layout'] 128 | state = module.params['state'] 129 | os_family = module.params['os_family'] 130 | operating_systems = module.params['operating_systems'] 131 | organizations = module.params['organizations'] 132 | locations = module.params['locations'] 133 | 134 | theforeman = init_foreman_client(module) 135 | 136 | data = dict(name=name) 137 | 138 | try: 139 | ptable = theforeman.search_partition_table(data=data) 140 | except ForemanError as e: 141 | module.fail_json(msg='Could not get partition table: {0}'.format(e.message)) 142 | 143 | if layout: 144 | data['layout'] = layout 145 | if os_family: 146 | data['os_family'] = os_family 147 | if organizations is not None: 148 | data['organization_ids'] = get_organization_ids(module, theforeman, organizations) 149 | if locations is not None: 150 | data['location_ids'] = get_location_ids(module, theforeman, locations) 151 | if operating_systems is not None: 152 | data['operatingsystem_ids'] = get_operatingsystem_ids(module, theforeman, operating_systems) 153 | 154 | if not ptable and state == 'present': 155 | try: 156 | ptable = theforeman.create_partition_table(data) 157 | except ForemanError as e: 158 | module.fail_json(msg='Could not create partition table: {0}'.format(e.message)) 159 | return True, ptable 160 | 161 | if ptable and state == 'absent': 162 | try: 163 | ptable = theforeman.delete_partition_table(id=ptable.get('id')) 164 | except ForemanError as e: 165 | module.fail_json(msg='Could not delete partition table: {0}'.format(e.message)) 166 | return True, ptable 167 | 168 | if ptable and state == 'present': 169 | try: 170 | ptable = theforeman.get_partition_table(id=ptable.get('id')) 171 | except ForemanError as e: 172 | module.fail_json(msg='Could not get partition table to update: {0}'.format(e.message)) 173 | if not ptables_equal(data, ptable): 174 | try: 175 | ptable = theforeman.update_partition_table(id=ptable.get('id'), data=data) 176 | except ForemanError as e: 177 | module.fail_json(msg='Could not update partition table: {0}'.format(e.message)) 178 | return True, ptable 179 | 180 | return False, ptable 181 | 182 | 183 | def main(): 184 | global module 185 | global theforeman 186 | 187 | module = AnsibleModule( 188 | argument_spec=dict( 189 | name=dict(type='str', required=True), 190 | layout=dict(type='str', required=False), 191 | os_family=dict(type='str', required=False), 192 | operating_systems=dict(type='list', required=False), 193 | organizations=dict(type='list', required=False), 194 | locations=dict(type='list', required=False), 195 | state=dict(type='str', default='present', choices=['present', 'absent']), 196 | foreman_host=dict(type='str', default='127.0.0.1'), 197 | foreman_port=dict(type='str', default='443'), 198 | foreman_user=dict(type='str', required=True), 199 | foreman_pass=dict(type='str', required=True, no_log=True), 200 | foreman_ssl=dict(type='bool', default=True) 201 | ), 202 | ) 203 | 204 | if not foremanclient_found: 205 | module.fail_json(msg='python-foreman module is required. See https://github.com/Nosmoht/python-foreman.') 206 | if has_import_error: 207 | module.fail_json(msg=import_error_msg) 208 | 209 | changed, ptable = ensure() 210 | module.exit_json(changed=changed, ptable=ptable) 211 | 212 | 213 | from ansible.module_utils.basic import * 214 | 215 | if __name__ == '__main__': 216 | main() 217 | -------------------------------------------------------------------------------- /foreman_realm.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # -*- coding: utf-8 -*- 3 | # 4 | # Ansible module to manage Foreman realm resources. 5 | # 6 | # This module is free software: you can redistribute it and/or modify 7 | # it under the terms of the GNU General Public License as published by 8 | # the Free Software Foundation, either version 3 of the License, or 9 | # (at your option) any later version. 10 | # 11 | # This software is distributed in the hope that it will be useful, 12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | # GNU General Public License for more details. 15 | # 16 | # You should have received a copy of the GNU General Public License 17 | # along with this software. If not, see . 18 | 19 | DOCUMENTATION = ''' 20 | --- 21 | module: foreman_realm 22 | short_description: Manage Foreman Realm using Foreman API v2 23 | description: 24 | - Create, update and delete Foreman Realms using Foreman API v2 25 | options: 26 | name: 27 | description: Realm name 28 | required: true 29 | realm_proxy: 30 | description: Realm smart proxy to use 31 | required: true 32 | realm_type: 33 | description: Realm type (e.g FreeIPA) 34 | required: true 35 | state: 36 | description: Realm state 37 | required: false 38 | default: 'present' 39 | choices: ['present', 'absent'] 40 | foreman_host: 41 | description: Hostname or IP address of Foreman system 42 | required: false 43 | default: 127.0.0.1 44 | foreman_port: 45 | description: Port of Foreman API 46 | required: false 47 | default: 443 48 | foreman_user: 49 | description: Username to be used to authenticate on Foreman 50 | required: true 51 | foreman_pass: 52 | description: Password to be used to authenticate user on Foreman 53 | required: true 54 | foreman_ssl: 55 | description: Enable SSL when connecting to Foreman API 56 | required: false 57 | default: true 58 | notes: 59 | - Requires the python-foreman package to be installed. See https://github.com/Nosmoht/python-foreman. 60 | version_added: "2.0" 61 | author: "Thomas Krahn (@nosmoht)" 62 | ''' 63 | 64 | EXAMPLES = ''' 65 | - name: Ensure Realm 66 | foreman_realm: 67 | name: MyRealm 68 | realm_proxy: http://localhost:3000 69 | realm_type: FreeIPA 70 | state: present 71 | foreman_host: foreman.example.com 72 | foreman_port: 443 73 | foreman_user: admin 74 | foreman_pass: secret 75 | ''' 76 | 77 | try: 78 | from foreman.foreman import * 79 | 80 | foremanclient_found = True 81 | except ImportError: 82 | foremanclient_found = False 83 | 84 | 85 | def get_resources(resource_type, resource_specs): 86 | result = [] 87 | for item in resource_specs: 88 | search_data = dict() 89 | if isinstance(item, dict): 90 | for key in item: 91 | search_data[key] = item[key] 92 | else: 93 | search_data['name'] = item 94 | try: 95 | resource = theforeman.search_resource(resource_type=resource_type, data=search_data) 96 | if not resource: 97 | module.fail_json( 98 | msg='Could not find resource type {resource_type} defined as {spec}'.format( 99 | resource_type=resource_type, 100 | spec=item)) 101 | result.append(resource) 102 | except ForemanError as e: 103 | module.fail_json(msg='Could not search resource type {resource_type} defined as {spec}: {error}'.format( 104 | resource_type=resource_type, spec=item, error=e.message)) 105 | return result 106 | 107 | 108 | def ensure(module): 109 | global theforeman 110 | 111 | name = module.params['name'] 112 | realm_proxy = module.params['realm_proxy'] 113 | realm_type = module.params['realm_type'] 114 | state = module.params['state'] 115 | 116 | foreman_host = module.params['foreman_host'] 117 | foreman_port = module.params['foreman_port'] 118 | foreman_user = module.params['foreman_user'] 119 | foreman_pass = module.params['foreman_pass'] 120 | foreman_ssl = module.params['foreman_ssl'] 121 | 122 | theforeman = Foreman(hostname=foreman_host, 123 | port=foreman_port, 124 | username=foreman_user, 125 | password=foreman_pass, 126 | ssl=foreman_ssl) 127 | 128 | data = {'name': name} 129 | try: 130 | realm = theforeman.search_realm(data=data) 131 | except ForemanError as e: 132 | module.fail_json(msg='Could not get realm: {0}'.format(e.message)) 133 | 134 | data['realm_type'] = realm_type 135 | data['realm_proxy_id'] = get_resources(resource_type='smart_proxies', resource_specs=[realm_proxy])[0].get('id') 136 | if not realm and state == 'present': 137 | try: 138 | realm = theforeman.create_realm(data=data) 139 | return True, realm 140 | except ForemanError as e: 141 | module.fail_json(msg='Could not create realm: {0}'.format(e.message)) 142 | 143 | if realm: 144 | if state == 'absent': 145 | try: 146 | realm = theforeman.delete_realm(id=realm.get('id')) 147 | return True, realm 148 | except ForemanError as e: 149 | module.fail_json(msg='Could not delete realm: {0}'.format(e.message)) 150 | 151 | elif not all(data[key] == realm[key] for key in data): 152 | try: 153 | realm = theforeman.update_realm(id=realm.get('id'), data=data) 154 | return True, realm 155 | except ForemanError as e: 156 | module.fail_json(msg='Could not update realm: {0}'.format(e.message)) 157 | 158 | return False, realm 159 | 160 | 161 | def main(): 162 | global module 163 | 164 | module = AnsibleModule( 165 | argument_spec=dict( 166 | name=dict(type='str', required=True), 167 | realm_proxy=dict(type='str', required=True), 168 | realm_type=dict(type='str', required=True), 169 | state=dict(type='str', default='present', choices=['present', 'absent']), 170 | foreman_host=dict(type='str', default='127.0.0.1'), 171 | foreman_port=dict(type='str', default='443'), 172 | foreman_user=dict(type='str', required=True), 173 | foreman_pass=dict(type='str', required=True, no_log=True), 174 | foreman_ssl=dict(type='bool', default=True) 175 | ), 176 | ) 177 | 178 | if not foremanclient_found: 179 | module.fail_json(msg='python-foreman module is required. See https://github.com/Nosmoht/python-foreman.') 180 | 181 | changed, realm = ensure(module) 182 | module.exit_json(changed=changed, realm=realm) 183 | 184 | 185 | from ansible.module_utils.basic import * 186 | 187 | if __name__ == '__main__': 188 | main() 189 | -------------------------------------------------------------------------------- /foreman_role.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # -*- coding: utf-8 -*- 3 | # 4 | # Ansible module to manage Foreman role resources. 5 | # 6 | # This module is free software: you can redistribute it and/or modify 7 | # it under the terms of the GNU General Public License as published by 8 | # the Free Software Foundation, either version 3 of the License, or 9 | # (at your option) any later version. 10 | # 11 | # This software is distributed in the hope that it will be useful, 12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | # GNU General Public License for more details. 15 | # 16 | # You should have received a copy of the GNU General Public License 17 | # along with this software. If not, see . 18 | 19 | DOCUMENTATION = ''' 20 | --- 21 | module: foreman_role 22 | short_description: Manage Foreman Role using Foreman API v2 23 | description: 24 | - Create, update and delete Foreman Role using Foreman API v2 25 | options: 26 | name: 27 | description: Role name 28 | required: false 29 | default: None 30 | state: 31 | description: Role state 32 | required: false 33 | default: 'present' 34 | choices: ['present', 'absent'] 35 | foreman_host: 36 | description: Hostname or IP address of Foreman system 37 | required: false 38 | default: 127.0.0.1 39 | foreman_port: 40 | description: Port of Foreman API 41 | required: false 42 | default: 443 43 | foreman_user: 44 | description: Username to be used to authenticate on Foreman 45 | required: true 46 | foreman_pass: 47 | description: Password to be used to authenticate user on Foreman 48 | required: true 49 | foreman_ssl: 50 | description: Enable SSL when connecting to Foreman API 51 | required: false 52 | default: true 53 | notes: 54 | - Requires the python-foreman package to be installed. See https://github.com/Nosmoht/python-foreman. 55 | version_added: "2.0" 56 | author: "Thomas Krahn (@nosmoht)" 57 | ''' 58 | 59 | EXAMPLES = ''' 60 | - name: Ensure Role 61 | foreman_role: 62 | name: MyRole 63 | state: present 64 | foreman_host: foreman.example.com 65 | foreman_port: 443 66 | foreman_user: admin 67 | foreman_pass: secret 68 | ''' 69 | 70 | try: 71 | from foreman.foreman import * 72 | 73 | foremanclient_found = True 74 | except ImportError: 75 | foremanclient_found = False 76 | 77 | 78 | def ensure(module): 79 | name = module.params['name'] 80 | state = module.params['state'] 81 | 82 | foreman_host = module.params['foreman_host'] 83 | foreman_port = module.params['foreman_port'] 84 | foreman_user = module.params['foreman_user'] 85 | foreman_pass = module.params['foreman_pass'] 86 | foreman_ssl = module.params['foreman_ssl'] 87 | 88 | theforeman = Foreman(hostname=foreman_host, 89 | port=foreman_port, 90 | username=foreman_user, 91 | password=foreman_pass, 92 | ssl=foreman_ssl) 93 | 94 | data = {'name': name} 95 | 96 | try: 97 | role = theforeman.search_role(data=data) 98 | except ForemanError as e: 99 | module.fail_json(msg='Could not get role: {0}'.format(e.message)) 100 | 101 | if not role and state == 'present': 102 | try: 103 | role = theforeman.create_role(data=data) 104 | return True, role 105 | except ForemanError as e: 106 | module.fail_json(msg='Could not create role: {0}'.format(e.message)) 107 | 108 | if role: 109 | if state == 'absent': 110 | try: 111 | role = theforeman.delete_role(id=role.get('id')) 112 | return True, role 113 | except ForemanError as e: 114 | module.fail_json(msg='Could not delete role: {0}'.format(e.message)) 115 | 116 | return False, role 117 | 118 | 119 | def main(): 120 | module = AnsibleModule( 121 | argument_spec=dict( 122 | name=dict(type='str', required=True), 123 | state=dict(type='str', default='present', choices=['present', 'absent']), 124 | foreman_host=dict(type='str', default='127.0.0.1'), 125 | foreman_port=dict(type='str', default='443'), 126 | foreman_user=dict(type='str', required=True), 127 | foreman_pass=dict(type='str', required=True, no_log=True), 128 | foreman_ssl=dict(type='bool', default=True) 129 | ), 130 | ) 131 | 132 | if not foremanclient_found: 133 | module.fail_json(msg='python-foreman module is required. See https://github.com/Nosmoht/python-foreman.') 134 | 135 | changed, role = ensure(module) 136 | module.exit_json(changed=changed, role=role) 137 | 138 | 139 | from ansible.module_utils.basic import * 140 | 141 | if __name__ == '__main__': 142 | main() 143 | -------------------------------------------------------------------------------- /foreman_setting.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # -*- coding: utf-8 -*- 3 | # 4 | # Ansible module to manage Foreman settings. 5 | # 6 | # This module is free software: you can redistribute it and/or modify 7 | # it under the terms of the GNU General Public License as published by 8 | # the Free Software Foundation, either version 3 of the License, or 9 | # (at your option) any later version. 10 | # 11 | # This software is distributed in the hope that it will be useful, 12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | # GNU General Public License for more details. 15 | # 16 | # You should have received a copy of the GNU General Public License 17 | # along with this software. If not, see . 18 | 19 | DOCUMENTATION = ''' 20 | --- 21 | module: foreman_setting 22 | short_description: Manage Foreman setting using Foreman API v2 23 | description: 24 | - Update Foreman settings using Foreman API v2 25 | options: 26 | name: 27 | description: Setting name 28 | required: true 29 | value: 30 | description: setting value 31 | required: false 32 | foreman_host: 33 | description: Hostname or IP address of Foreman system 34 | required: false 35 | default: 127.0.0.1 36 | foreman_port: 37 | description: Port of Foreman API 38 | required: false 39 | default: 443 40 | foreman_user: 41 | description: Username to be used to authenticate on Foreman 42 | required: true 43 | foreman_pass: 44 | description: Password to be used to authenticate user on Foreman 45 | required: true 46 | foreman_ssl: 47 | description: Enable SSL when connecting to Foreman API 48 | required: false 49 | default: true 50 | notes: 51 | - Requires the python-foreman package to be installed. See https://github.com/Nosmoht/python-foreman. 52 | version_added: "2.0" 53 | author: "Guido Günther " 54 | ''' 55 | 56 | EXAMPLES = ''' 57 | - name: Ensure Setting 58 | foreman_setting: 59 | name: outofsync_interval 60 | value: 10 61 | foreman_host: foreman.example.com 62 | foreman_port: 443 63 | foreman_user: admin 64 | foreman_pass: secret 65 | ''' 66 | 67 | try: 68 | from foreman.foreman import * 69 | foremanclient_found = True 70 | except ImportError: 71 | foremanclient_found = False 72 | 73 | 74 | def update_setting(setting, data): 75 | try: 76 | setting = theforeman.update_setting(id=setting['id'], data=data) 77 | except ForemanError as e: 78 | module.fail_json(msg='Could not update setting: {0}'.format(e.message)) 79 | return setting 80 | 81 | 82 | def fake_setting(setting, data): 83 | setting['value'] = data['value'] 84 | 85 | 86 | def ensure(module): 87 | global theforeman 88 | 89 | name = module.params['name'] 90 | value = module.params['value'] 91 | 92 | foreman_host = module.params['foreman_host'] 93 | foreman_port = module.params['foreman_port'] 94 | foreman_user = module.params['foreman_user'] 95 | foreman_pass = module.params['foreman_pass'] 96 | foreman_ssl = module.params['foreman_ssl'] 97 | 98 | theforeman = Foreman(hostname=foreman_host, 99 | port=foreman_port, 100 | username=foreman_user, 101 | password=foreman_pass, 102 | ssl=foreman_ssl) 103 | 104 | data = {'name': name} 105 | try: 106 | setting = theforeman.search_setting(data=data) 107 | except ForemanError as e: 108 | module.fail_json(msg='Could not get setting: {0}'.format(e.message)) 109 | 110 | if not setting: 111 | module.fail_json(msg='Setting %s does not exist' % name) 112 | 113 | if isinstance(setting['value'], (bool, str, int)): 114 | data['value'] = type(setting['value'])(value) 115 | else: 116 | data['value'] = value 117 | 118 | if data['value'] != setting['value']: 119 | if module.check_mode: 120 | setting = fake_setting(setting, data) 121 | else: 122 | setting = update_setting(setting, data) 123 | return True, setting 124 | return False, setting 125 | 126 | 127 | def main(): 128 | global module 129 | 130 | module = AnsibleModule( 131 | argument_spec=dict( 132 | name=dict(type='str', required=True), 133 | value=dict(type='str', required=True), 134 | foreman_host=dict(type='str', default='127.0.0.1'), 135 | foreman_port=dict(type='str', default='443'), 136 | foreman_user=dict(type='str', required=True), 137 | foreman_pass=dict(type='str', required=True, no_log=True), 138 | foreman_ssl=dict(type='bool', default=True) 139 | ), 140 | supports_check_mode=True, 141 | ) 142 | 143 | if not foremanclient_found: 144 | module.fail_json(msg='python-foreman module is required. See https://github.com/Nosmoht/python-foreman.') 145 | 146 | changed, setting = ensure(module) 147 | module.exit_json(changed=changed, setting=setting) 148 | 149 | # import module snippets 150 | from ansible.module_utils.basic import * 151 | 152 | if __name__ == '__main__': 153 | main() 154 | -------------------------------------------------------------------------------- /foreman_smart_proxy.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # -*- coding: utf-8 -*- 3 | # 4 | # Ansible module to manage Foreman smart proxy resources. 5 | # 6 | # This module is free software: you can redistribute it and/or modify 7 | # it under the terms of the GNU General Public License as published by 8 | # the Free Software Foundation, either version 3 of the License, or 9 | # (at your option) any later version. 10 | # 11 | # This software is distributed in the hope that it will be useful, 12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | # GNU General Public License for more details. 15 | # 16 | # You should have received a copy of the GNU General Public License 17 | # along with this software. If not, see . 18 | 19 | DOCUMENTATION = ''' 20 | --- 21 | module: foreman_smart_proxy 22 | short_description: Manage Foreman smart proxy resources using Foreman API v2 23 | description: 24 | - Create and delete Foreman smart proxy resources using Foreman API v2 25 | options: 26 | name: 27 | description: Smart proxy name 28 | required: true 29 | state: 30 | description: Smart proxy state 31 | required: false 32 | default: present 33 | choices: ["present", "absent"] 34 | url: 35 | description: Smart proxy URL 36 | required: false 37 | default: None 38 | locations: List of locations the smart_proxy should be assigned to 39 | required: false 40 | default: None 41 | organizations: List of organizations the smart_proxy should be assigned to 42 | required: false 43 | default: None 44 | foreman_host: 45 | description: Hostname or IP address of Foreman system 46 | required: false 47 | default: 127.0.0.1 48 | foreman_port: 49 | description: Port of Foreman API 50 | required: false 51 | default: 443 52 | foreman_user: 53 | description: Username to be used to authenticate on Foreman 54 | required: true 55 | foreman_pass: 56 | description: Password to be used to authenticate user on Foreman 57 | required: true 58 | foreman_ssl: 59 | description: Enable SSL when connecting to Foreman API 60 | required: false 61 | default: true 62 | notes: 63 | - Requires the python-foreman package to be installed. See https://github.com/Nosmoht/python-foreman. 64 | version_added: "2.0" 65 | author: "Thomas Krahn (@nosmoht)" 66 | ''' 67 | 68 | try: 69 | from foreman.foreman import * 70 | 71 | foremanclient_found = True 72 | except ImportError: 73 | foremanclient_found = False 74 | 75 | try: 76 | from ansible.module_utils.foreman_utils import * 77 | 78 | has_import_error = False 79 | except ImportError as e: 80 | has_import_error = True 81 | import_error_msg = str(e) 82 | 83 | 84 | def smart_proxies_equal(data, smart_proxy): 85 | comparable_keys = set(data.keys()).intersection(set(['url'])) 86 | if not all(data.get(key, None) == smart_proxy.get(key, None) for key in comparable_keys): 87 | return False 88 | if not organizations_equal(data, smart_proxy): 89 | return False 90 | if not locations_equal(data, smart_proxy): 91 | return False 92 | return True 93 | 94 | 95 | def ensure(module): 96 | name = module.params['name'] 97 | url = module.params['url'] 98 | state = module.params['state'] 99 | organizations = module.params['organizations'] 100 | locations = module.params['locations'] 101 | 102 | theforeman = init_foreman_client(module) 103 | 104 | data = {'name': name} 105 | 106 | try: 107 | smart_proxy = theforeman.search_smart_proxy(data=data) 108 | if smart_proxy: 109 | smart_proxy = theforeman.get_smart_proxy(id=smart_proxy.get('id')) 110 | except ForemanError as e: 111 | module.fail_json(msg='Could not get smart proxy: {0}'.format(e.message)) 112 | 113 | if organizations: 114 | data['organization_ids'] = get_organization_ids(module, theforeman, organizations) 115 | 116 | if locations: 117 | data['location_ids'] = get_location_ids(module, theforeman, locations) 118 | 119 | data['url'] = url 120 | 121 | if not smart_proxy and state == 'present': 122 | try: 123 | smart_proxy = theforeman.create_smart_proxy(data=data) 124 | return True, smart_proxy 125 | except ForemanError as e: 126 | module.fail_json(msg='Could not create smart proxy: {0}'.format(e.message)) 127 | 128 | if smart_proxy: 129 | if state == 'absent': 130 | try: 131 | smart_proxy = theforeman.delete_smart_proxy(id=smart_proxy.get('id')) 132 | return True, smart_proxy 133 | except ForemanError as e: 134 | module.fail_json(msg='Could not delete smart proxy: {0}'.format(e.message)) 135 | 136 | if not smart_proxies_equal(data, smart_proxy): 137 | try: 138 | smart_proxy = theforeman.update_smart_proxy(id=smart_proxy.get('id'), data=data) 139 | return True, smart_proxy 140 | except ForemanError as e: 141 | module.fail_json(msg='Could not update smart proxy: {0}'.format(e.message)) 142 | return False, smart_proxy 143 | 144 | 145 | def main(): 146 | module = AnsibleModule( 147 | argument_spec=dict( 148 | name=dict(type='str', required=True), 149 | url=dict(type='str', required=False), 150 | state=dict(type='str', default='present', choices=['present', 'absent']), 151 | organizations=dict(type='list', required=False), 152 | locations=dict(type='list', required=False), 153 | foreman_host=dict(type='str', default='127.0.0.1'), 154 | foreman_port=dict(type='str', default='443'), 155 | foreman_user=dict(type='str', required=True), 156 | foreman_pass=dict(type='str', required=True, no_log=True), 157 | foreman_ssl=dict(type='bool', default=True) 158 | ), 159 | ) 160 | 161 | if not foremanclient_found: 162 | module.fail_json(msg='python-foreman module is required. See https://github.com/Nosmoht/python-foreman.') 163 | 164 | changed, smart_proxy = ensure(module) 165 | module.exit_json(changed=changed, smart_proxy=smart_proxy) 166 | 167 | 168 | from ansible.module_utils.basic import * 169 | 170 | if __name__ == '__main__': 171 | main() 172 | -------------------------------------------------------------------------------- /foreman_subnet.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # -*- coding: utf-8 -*- 3 | # 4 | # Ansible module to manage Foreman subnet resources. 5 | # 6 | # This module is free software: you can redistribute it and/or modify 7 | # it under the terms of the GNU General Public License as published by 8 | # the Free Software Foundation, either version 3 of the License, or 9 | # (at your option) any later version. 10 | # 11 | # This software is distributed in the hope that it will be useful, 12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | # GNU General Public License for more details. 15 | # 16 | # You should have received a copy of the GNU General Public License 17 | # along with this software. If not, see . 18 | 19 | DOCUMENTATION = ''' 20 | --- 21 | module: foreman_architecture 22 | short_description: Manage Foreman Architectures using Foreman API v2 23 | description: 24 | - Create and delete Foreman Architectures using Foreman API v2 25 | options: 26 | name: 27 | description: Subnet name 28 | required: True 29 | network: 30 | description: Subnet network 31 | required: False 32 | default: None 33 | mask: 34 | description: Netmask for this subnet 35 | required: False 36 | default: None 37 | gateway: 38 | description: Gateway for this subnet 39 | required: False 40 | default: None 41 | dns_primary: 42 | description: Primary DNS for this subnet 43 | required: False 44 | default: None 45 | dns_secondary: 46 | description: Secondary DNS for this subnet 47 | required: False 48 | default: None 49 | domains: 50 | description: Domains in which this subnet is part 51 | required: False 52 | default: None 53 | ipam: 54 | description: Enable IP Address auto suggestion for this subnet 55 | required: False 56 | default: None 57 | choices: ['DHCP', 'Internal DB', 'Random DB', 'None']), 58 | boot_mode: 59 | description: Default boot mode for interfaces assigned to this subnet 60 | required: False 61 | default: 'DHCP' 62 | choices: ['DHCP', 'Static']), 63 | ip_from: 64 | description: Starting IP Address for IP auto suggestion 65 | required: False 66 | default: None 67 | ip_to: 68 | description: Ending IP Address for IP auto suggestion 69 | required: False 70 | default: None 71 | dhcp_proxy: 72 | description: DHCP smart proxy to use for this subnet 73 | required: False 74 | default: None 75 | dns_proxy: 76 | description: DNS smart proxy to use for this subnet 77 | required: False 78 | default: None 79 | discovery_proxy: 80 | description: Discovery smart proxy to use for this subnet (requires foreman discover plugin) 81 | required: False 82 | default: None 83 | tftp_proxy: 84 | description: TFTP smart proxy to use for this subnet 85 | required: False 86 | default: None 87 | state: 88 | description: State of subnet 89 | required: false 90 | default: present 91 | choices: ["present", "absent"] 92 | vlanid: 93 | description: VLAN ID for this subnet 94 | required: False 95 | default: None 96 | locations: List of locations the subnet should be assigned to 97 | required: false 98 | default: None 99 | organizations: List of organizations the subnet should be assigned to 100 | required: false 101 | default: None 102 | foreman_host: 103 | description: Hostname or IP address of Foreman system 104 | required: false 105 | default: 127.0.0.1 106 | foreman_port: 107 | description: Port of Foreman API 108 | required: false 109 | default: 443 110 | foreman_user: 111 | description: Username to be used to authenticate on Foreman 112 | required: true 113 | foreman_pass: 114 | description: Password to be used to authenticate user on Foreman 115 | required: true 116 | foreman_ssl: 117 | description: Enable SSL when connecting to Foreman API 118 | required: false 119 | default: true 120 | notes: 121 | - Requires the python-foreman package to be installed. See https://github.com/Nosmoht/python-foreman. 122 | version_added: "2.0" 123 | author: "Thomas Krahn (@nosmoht)" 124 | ''' 125 | 126 | EXAMPLES = ''' 127 | - name: Ensure Subnet 128 | foreman_subnet: 129 | name: MySubnet 130 | network: 192.168.123.0 131 | mask: 255.255.255.0 132 | dns_primary: 192.168.123.1 133 | dns_secondary: 192.168.123.2 134 | domains: 135 | - foo.example.com 136 | ipam: DHCP 137 | boot_mode: Static 138 | ip_from: 192.168.123.3 139 | ip_to: 192.168.123.253 140 | gateway: 192.168.123.254 141 | vlanid: 123 142 | state: present 143 | locations: 144 | - Tardis 145 | organizations: 146 | - Dalek Inc 147 | - Cybermen 148 | foreman_host: 127.0.0.1 149 | foreman_port: 443 150 | foreman_user: admin 151 | foreman_pass: secret 152 | ''' 153 | 154 | try: 155 | from foreman.foreman import * 156 | except ImportError: 157 | foremanclient_found = False 158 | else: 159 | foremanclient_found = True 160 | 161 | try: 162 | from ansible.module_utils.foreman_utils import * 163 | 164 | has_import_error = False 165 | except ImportError as e: 166 | has_import_error = True 167 | import_error_msg = str(e) 168 | 169 | 170 | def domains_equal(data, subnet): 171 | data_domains = list(map(lambda d: d['name'], data['domains'])).sort() 172 | subnet_domains = list(map(lambda d: d['name'], subnet['domains'])).sort() 173 | if data_domains != subnet_domains: 174 | return False 175 | return True 176 | 177 | 178 | def subnets_equal(data, subnet): 179 | comparable_keys = ['name', 'dns_primary', 'dns_secondary', 'gateway', 'ipam', 'boot_mode', 'mask', 'network', 180 | 'vlanid', 'from', 'to', 'tftp_id', 'dns_id', 'dhcp_id', 'discovery_id'] 181 | if not all(data.get(key, None) == subnet.get(key, None) for key in comparable_keys): 182 | return False 183 | if not domains_equal(data, subnet): 184 | return False 185 | if not organizations_equal(data, subnet): 186 | return False 187 | if not locations_equal(data, subnet): 188 | return False 189 | return True 190 | 191 | 192 | def get_resources(resource_type, resource_specs, theforeman): 193 | result = [] 194 | for item in resource_specs: 195 | search_data = dict() 196 | if isinstance(item, dict): 197 | for key in item: 198 | search_data[key] = item[key] 199 | else: 200 | search_data['name'] = item 201 | try: 202 | resource = theforeman.search_resource(resource_type=resource_type, data=search_data) 203 | if not resource: 204 | module.fail_json( 205 | msg='Could not find resource type {resource_type} defined as {spec}'.format( 206 | resource_type=resource_type, 207 | spec=item)) 208 | result.append(resource) 209 | except ForemanError as e: 210 | module.fail_json(msg='Could not search resource type {resource_type} defined as {spec}: {error}'.format( 211 | resource_type=resource_type, spec=item, error=e.message)) 212 | return result 213 | 214 | 215 | def prepare_data(data, module, theforeman): 216 | for key in ['dns_primary', 'dns_secondary', 'gateway', 'ipam', 'boot_mode', 'mask', 'network', 217 | 'vlanid', 'domains']: 218 | if key in module.params: 219 | data[key] = module.params[key] 220 | if 'ip_from' in module.params: 221 | data['from'] = module.params['ip_from'] 222 | if 'ip_to' in module.params: 223 | data['to'] = module.params['ip_to'] 224 | if 'domains' in module.params and module.params['domains']: 225 | data['domains'] = get_resources(resource_type='domains', resource_specs=module.params['domains'], 226 | theforeman=theforeman) 227 | for proxy_type in ['dns', 'dhcp', 'tftp', 'discovery']: 228 | key = "{0}_proxy".format(proxy_type) 229 | if key in module.params: 230 | id_key = "{0}_id".format(proxy_type) 231 | if module.params[key]: 232 | data[id_key] = get_resources(resource_type='smart_proxies', resource_specs=[module.params[key]], 233 | theforeman=theforeman)[0].get('id') 234 | else: 235 | data[id_key] = None 236 | return data 237 | 238 | 239 | def ensure(module): 240 | name = module.params['name'] 241 | state = module.params['state'] 242 | locations = module.params['locations'] 243 | organizations = module.params['organizations'] 244 | 245 | theforeman = init_foreman_client(module) 246 | 247 | data = {'name': name} 248 | 249 | try: 250 | subnet = theforeman.search_subnet(data=data) 251 | if subnet: 252 | subnet = theforeman.get_subnet(id=subnet.get('id')) 253 | except ForemanError as e: 254 | module.fail_json(msg='Could not get subnet: {0}'.format(e.message)) 255 | 256 | if organizations: 257 | data['organization_ids'] = get_organization_ids(module, theforeman, organizations) 258 | 259 | if locations: 260 | data['location_ids'] = get_location_ids(module, theforeman, locations) 261 | 262 | data = prepare_data(data, module, theforeman) 263 | 264 | if not subnet and state == 'present': 265 | try: 266 | subnet = theforeman.create_subnet(data=data) 267 | return True, subnet 268 | except ForemanError as e: 269 | module.fail_json(msg='Could not create subnet: {0}'.format(e.message)) 270 | 271 | if subnet: 272 | if state == 'absent': 273 | try: 274 | subnet = theforeman.delete_subnet(id=subnet.get('id')) 275 | return True, subnet 276 | except ForemanError as e: 277 | module.fail_json(msg='Could not delete subnet: {0}'.format(e.message)) 278 | 279 | if not subnets_equal(data, subnet): 280 | try: 281 | subnet = theforeman.update_subnet(id=subnet.get('id'), data=data) 282 | return True, subnet 283 | except ForemanError as e: 284 | module.fail_json(msg='Could not update subnet: {0}'.format(e.message)) 285 | 286 | return False, subnet 287 | 288 | 289 | def main(): 290 | global module 291 | 292 | module = AnsibleModule( 293 | argument_spec=dict( 294 | dhcp_proxy=dict(type='str', required=False), 295 | dns_proxy=dict(type='str', required=False), 296 | discovery_proxy=dict(type='str', required=False), 297 | dns_primary=dict(type='str', required=False), 298 | dns_secondary=dict(type='str', required=False), 299 | domains=dict(type='list', required=False), 300 | gateway=dict(type='str', required=False), 301 | name=dict(type='str', required=True), 302 | network=dict(type='str', required=False), 303 | mask=dict(type='str', required=False), 304 | ipam=dict(type='str', required=False, choices=['DHCP', 'Internal DB', 'Random DB', 'None']), 305 | boot_mode=dict(type='str', required=False, choices=['DHCP', 'Static'], default='DHCP'), 306 | ip_from=dict(type='str', required=False), 307 | ip_to=dict(type='str', required=False), 308 | state=dict(type='str', default='present', choices=['present', 'absent']), 309 | tftp_proxy=dict(type='str', required=False), 310 | vlanid=dict(type='str', default=None), 311 | locations=dict(type='list', required=False), 312 | organizations=dict(type='list', required=False), 313 | foreman_host=dict(type='str', default='127.0.0.1'), 314 | foreman_port=dict(type='str', default='443'), 315 | foreman_user=dict(type='str', required=True), 316 | foreman_pass=dict(type='str', required=True, no_log=True), 317 | foreman_ssl=dict(type='bool', default=True) 318 | ), 319 | ) 320 | 321 | if not foremanclient_found: 322 | module.fail_json(msg='python-foreman module is required. See https://github.com/Nosmoht/python-foreman.') 323 | 324 | changed, subnet = ensure(module) 325 | module.exit_json(changed=changed, subnet=subnet) 326 | 327 | 328 | from ansible.module_utils.basic import * 329 | 330 | if __name__ == '__main__': 331 | main() 332 | -------------------------------------------------------------------------------- /foreman_user.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # -*- coding: utf-8 -*- 3 | # 4 | # Ansible module to manage Foreman user resources. 5 | # 6 | # This module is free software: you can redistribute it and/or modify 7 | # it under the terms of the GNU General Public License as published by 8 | # the Free Software Foundation, either version 3 of the License, or 9 | # (at your option) any later version. 10 | # 11 | # This software is distributed in the hope that it will be useful, 12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | # GNU General Public License for more details. 15 | # 16 | # You should have received a copy of the GNU General Public License 17 | # along with this software. If not, see . 18 | 19 | DOCUMENTATION = ''' 20 | --- 21 | module: foreman_user 22 | short_description: Manage Foreman Users using Foreman API v2 23 | description: 24 | - Manage Foreman users using Foreman API v2 25 | options: 26 | admin: 27 | description: Is an admin account 28 | required: false 29 | default: False 30 | choices: [True, False] 31 | auth: 32 | description: Authorization method 33 | required: false 34 | default: 'Internal' 35 | login: 36 | description: Name of user 37 | required: true 38 | default: None 39 | aliases: ['name'] 40 | firstname: 41 | description: User's firstname 42 | required: false 43 | default: None 44 | lastname: 45 | description: User's lastname 46 | required: false 47 | default: None 48 | mail: 49 | description: Mail address 50 | required: false 51 | default: None 52 | password: 53 | description: Password 54 | required: false 55 | default: None 56 | roles: 57 | description: Roles assigned to the user 58 | required: false 59 | default: None 60 | state: 61 | description: State of user 62 | required: false 63 | default: present 64 | choices: ["present", "absent"] 65 | foreman_host: 66 | description: Hostname or IP address of Foreman system 67 | required: false 68 | default: 127.0.0.1 69 | foreman_port: 70 | description: Port of Foreman API 71 | required: false 72 | default: 443 73 | foreman_user: 74 | description: Username to be used to authenticate on Foreman 75 | required: true 76 | foreman_pass: 77 | description: Password to be used to authenticate user on Foreman 78 | required: true 79 | foreman_ssl: 80 | description: Enable SSL when connecting to Foreman API 81 | required: false 82 | default: true 83 | notes: 84 | - Requires the python-foreman package to be installed. See https://github.com/Nosmoht/python-foreman. 85 | version_added: "2.0" 86 | author: "Thomas Krahn (@nosmoht)" 87 | ''' 88 | 89 | EXAMPLES = ''' 90 | - name: Ensure foo user is present 91 | foreman_user: 92 | name: foo 93 | state: present 94 | roles: 95 | - "Viewer" 96 | foreman_user: admin 97 | foreman_pass: secret 98 | ''' 99 | 100 | try: 101 | from foreman.foreman import * 102 | 103 | foremanclient_found = True 104 | except ImportError: 105 | foremanclient_found = False 106 | 107 | 108 | def get_roles(module, theforeman, roles): 109 | result = list() 110 | for item in roles: 111 | search_data = dict() 112 | if isinstance(item, dict): 113 | search_data = item 114 | else: 115 | search_data['name'] = item 116 | try: 117 | role = theforeman.search_role(data=search_data) 118 | if not role: 119 | module.fail_json(msg='Could not find role {0}'.format(item)) 120 | result.append(role) 121 | except ForemanError as e: 122 | module.fail_json(msg='Could not search role {0}: {1}'.format(item, e.message)) 123 | return result 124 | 125 | 126 | def extract_key_value_from_dict_array(a, key): 127 | result = list() 128 | for d in a: 129 | result.append(d.get(key, None)) 130 | return result 131 | 132 | 133 | def equal_roles(assigned_roles, defined_roles): 134 | ar = set(extract_key_value_from_dict_array(assigned_roles, 'name')) 135 | dr = set(extract_key_value_from_dict_array(defined_roles, 'name')) 136 | return ar.issubset(dr) and dr.issubset(ar) 137 | 138 | 139 | def ensure(module): 140 | login = module.params['login'] 141 | state = module.params['state'] 142 | roles = module.params['roles'] 143 | 144 | foreman_host = module.params['foreman_host'] 145 | foreman_port = module.params['foreman_port'] 146 | foreman_user = module.params['foreman_user'] 147 | foreman_pass = module.params['foreman_pass'] 148 | foreman_ssl = module.params['foreman_ssl'] 149 | 150 | user_options = ['admin', 'auth_source_name', 'firstname', 'lastname', 'mail'] 151 | 152 | theforeman = Foreman(hostname=foreman_host, 153 | port=foreman_port, 154 | username=foreman_user, 155 | password=foreman_pass, 156 | ssl=foreman_ssl) 157 | 158 | data = dict(login=login) 159 | 160 | try: 161 | # Search the user. If it does exist get detailed information 162 | found = theforeman.search_user(data=data) 163 | if found: 164 | user = theforeman.get_user(id=found.get('id')) 165 | else: 166 | user = None 167 | except ForemanError as e: 168 | module.fail_json(msg='Could not get user: {0}'.format(e.message)) 169 | 170 | # Compare assigned values. password is not returned by Foreman and must be handled different 171 | for key in user_options: 172 | if key in module.params: 173 | data[key] = module.params[key] 174 | 175 | if roles: 176 | data['roles'] = get_roles(module, theforeman, roles) 177 | else: 178 | data['roles'] = [] 179 | 180 | if not user and state == 'present': 181 | try: 182 | data['password'] = module.params['password'] 183 | user = theforeman.create_user(data=data) 184 | return True, user 185 | except ForemanError as e: 186 | module.fail_json(msg='Could not create user: {0}'.format(e.message)) 187 | 188 | if user: 189 | if state == 'absent': 190 | try: 191 | user, theforeman.delete_user(id=user.get('id')) 192 | return True, user 193 | except ForemanError as e: 194 | module.fail_json(msg='Could not delete user: {0}'.format(e.message)) 195 | 196 | if (not all(user.get(key, data[key]) == data[key] for key in user_options)) or ( 197 | not equal_roles(defined_roles=data.get('roles'), assigned_roles=user.get('roles'))): 198 | try: 199 | # module.fail_json(msg='{0}\n{1}'.format(user.get('roles'), data.get('roles'))) 200 | user = theforeman.update_user(id=user.get('id'), data=data) 201 | return True, user 202 | except ForemanError as e: 203 | module.fail_json(msg='Could not update user: {0}'.format(e.message)) 204 | 205 | return False, user 206 | 207 | 208 | def main(): 209 | module = AnsibleModule( 210 | argument_spec=dict( 211 | admin=dict(type='str', required=False, default=False), 212 | auth_source_name=dict(type='str', default='Internal', aliases=['auth']), 213 | login=dict(type='str', required=True, aliases=['name']), 214 | firstname=dict(type='str', required=False), 215 | lastname=dict(type='str', required=False), 216 | mail=dict(type='str', required=False), 217 | state=dict(type='str', default='present', choices=['present', 'absent']), 218 | password=dict(type='str', required=False, no_log=True), 219 | roles=dict(type='list', required=False), 220 | foreman_host=dict(type='str', default='127.0.0.1'), 221 | foreman_port=dict(type='str', default='443'), 222 | foreman_user=dict(type='str', required=True), 223 | foreman_pass=dict(type='str', required=True, no_log=True), 224 | foreman_ssl=dict(type='bool', default=True) 225 | ), 226 | ) 227 | 228 | if not foremanclient_found: 229 | module.fail_json(msg='python-foreman module is required. See https://github.com/Nosmoht/python-foreman.') 230 | 231 | changed, user = ensure(module) 232 | module.exit_json(changed=changed, user=user) 233 | 234 | 235 | from ansible.module_utils.basic import * 236 | 237 | if __name__ == '__main__': 238 | main() 239 | -------------------------------------------------------------------------------- /foreman_usergroup.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # -*- coding: utf-8 -*- 3 | # 4 | # Ansible module to manage Foreman usergroup resources. 5 | # 6 | # This module is free software: you can redistribute it and/or modify 7 | # it under the terms of the GNU General Public License as published by 8 | # the Free Software Foundation, either version 3 of the License, or 9 | # (at your option) any later version. 10 | # 11 | # This software is distributed in the hope that it will be useful, 12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | # GNU General Public License for more details. 15 | # 16 | # You should have received a copy of the GNU General Public License 17 | # along with this software. If not, see . 18 | # 19 | # (C) 2017 Guido Günther 20 | 21 | DOCUMENTATION = ''' 22 | --- 23 | module: foreman_usergroup 24 | short_description: Manage Foreman Usergroup using Foreman API v2 25 | description: 26 | - Create and delete Foreman Usergroups using Foreman API v2 27 | options: 28 | name: 29 | description: name of the usergroup 30 | required: true 31 | users: 32 | description: user of this usergroup. Users are identified by their 33 | login. 34 | required: false 35 | usergroups: 36 | description: usergroups of this usergroup. Usergroups can be 37 | nested. 38 | required: false 39 | roles: 40 | description: roles assigned to this usergroup 41 | required: false 42 | state: 43 | description: State of usergroup 44 | required: false 45 | default: present 46 | choices: ["present", "absent"] 47 | foreman_host: 48 | description: Hostname or IP address of Foreman system 49 | required: false 50 | default: 127.0.0.1 51 | foreman_port: 52 | description: Port of Foreman API 53 | required: false 54 | default: 443 55 | foreman_user: 56 | description: Username to be used to authenticate on Foreman 57 | required: true 58 | foreman_pass: 59 | description: Password to be used to authenticate user on Foreman 60 | required: true 61 | foreman_ssl: 62 | description: Enable SSL when connecting to Foreman API 63 | required: false 64 | default: true 65 | notes: 66 | - Requires the python-foreman package to be installed. See https://github.com/Nosmoht/python-foreman. 67 | - This module does currently not update already existing groups 68 | version_added: "2.0" 69 | author: "Thomas Krahn (@nosmoht)" 70 | ''' 71 | 72 | EXAMPLES = ''' 73 | - name: Ensure a simple usergroup 74 | foreman_usergroup: 75 | name: poweradmins 76 | roles: 77 | - "Edit hostgroups" 78 | - Manager 79 | users: 80 | - user1 81 | - user2 82 | state: present 83 | foreman_host: 127.0.0.1 84 | foreman_port: 443 85 | foreman_user: admin 86 | foreman_pass: secret 87 | ''' 88 | 89 | try: 90 | from foreman.foreman import * 91 | 92 | foremanclient_found = True 93 | except ImportError: 94 | foremanclient_found = False 95 | 96 | 97 | def get_ids(module, theforeman, res_type, names, field='name'): 98 | result = [] 99 | try: 100 | searcher = getattr(theforeman, "search_{0}".format(res_type)) 101 | except AttributeError: 102 | module.fail_json(msg="Don't know how to search for {0}".format(res_type)) 103 | 104 | for name in names: 105 | try: 106 | res = searcher(data={field: name}) 107 | if not res: 108 | module.fail_json(msg="Could not find '{0}' of type {1}".format(name, res_type)) 109 | result.append(res.get('id')) 110 | except ForemanError as e: 111 | module.fail_json(msg="Could not get '{0}' of type {1}: {2}".format(name, 112 | res_type, 113 | e.message)) 114 | return result 115 | 116 | 117 | def ensure(module): 118 | name = module.params['name'] 119 | state = module.params['state'] 120 | roles = module.params['roles'] 121 | users = module.params['users'] 122 | usergroups = module.params['usergroups'] 123 | 124 | foreman_host = module.params['foreman_host'] 125 | foreman_port = module.params['foreman_port'] 126 | foreman_user = module.params['foreman_user'] 127 | foreman_pass = module.params['foreman_pass'] 128 | foreman_ssl = module.params['foreman_ssl'] 129 | 130 | theforeman = Foreman(hostname=foreman_host, 131 | port=foreman_port, 132 | username=foreman_user, 133 | password=foreman_pass, 134 | ssl=foreman_ssl) 135 | 136 | data = dict(name=name) 137 | 138 | try: 139 | usergroup = theforeman.search_usergroup(data) 140 | except ForemanError as e: 141 | module.fail_json(msg='Could not get usergroup: {0}'.format(e.message)) 142 | 143 | if not usergroup and state == 'present': 144 | if usergroups: 145 | data['usergroup_ids'] = get_ids(module, theforeman, 'usergroup', usergroups) 146 | if roles: 147 | data['role_ids'] = get_ids(module, theforeman, 'role', roles) 148 | if users: 149 | data['user_ids'] = get_ids(module, theforeman, 'user', users, field='login') 150 | 151 | try: 152 | usergroup = theforeman.create_usergroup(data=data) 153 | return True, usergroup 154 | except ForemanError as e: 155 | module.fail_json(msg='Could not create usergroup: {0}'.format(e.message)) 156 | 157 | if usergroup and state == 'absent': 158 | try: 159 | usergroup = theforeman.delete_usergroup(id=usergroup['id']) 160 | except ForemanError as e: 161 | module.fail_json(msg='Could not delete usergroup: {0}'.format(e.message)) 162 | return True, usergroup 163 | 164 | return False, usergroup 165 | 166 | 167 | def main(): 168 | module = AnsibleModule( 169 | argument_spec=dict( 170 | state=dict(type='str', default='present', choices=['present', 'absent']), 171 | name=dict(type='str', required=True), 172 | roles=dict(type='list', required=False), 173 | users=dict(type='list', required=False), 174 | usergroups=dict(type='list', required=False), 175 | foreman_host=dict(type='str', default='127.0.0.1'), 176 | foreman_port=dict(type='str', default='443'), 177 | foreman_user=dict(type='str', required=True), 178 | foreman_pass=dict(type='str', required=True, no_log=True), 179 | foreman_ssl=dict(type='bool', default=True) 180 | ), 181 | ) 182 | 183 | if not foremanclient_found: 184 | module.fail_json(msg='python-foreman module is required. See https://github.com/Nosmoht/python-foreman.') 185 | 186 | changed, usergroup = ensure(module) 187 | module.exit_json(changed=changed, usergroup=usergroup) 188 | 189 | 190 | from ansible.module_utils.basic import * 191 | 192 | if __name__ == '__main__': 193 | main() 194 | -------------------------------------------------------------------------------- /module_utils/foreman_utils.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # (c) Radim Janča (Cesnet) 2018 3 | 4 | try: 5 | from foreman.foreman import * 6 | except ImportError: 7 | module.fail_json(msg='python-foreman module is required. See https://github.com/Nosmoht/python-foreman.') 8 | 9 | 10 | def init_foreman_client(module): 11 | return Foreman(hostname=module.params['foreman_host'], 12 | port=module.params['foreman_port'], 13 | username=module.params['foreman_user'], 14 | password=module.params['foreman_pass'], 15 | ssl=module.params['foreman_ssl']) 16 | 17 | 18 | def equal_dict_lists(l1, l2, compare_key='name'): 19 | s1 = set(map(lambda x: x[compare_key], l1)) 20 | s2 = set(map(lambda x: x[compare_key], l2)) 21 | return s1 == s2 22 | 23 | 24 | def dict_list_to_list(alist, key): 25 | result = list() 26 | if alist: 27 | for item in alist: 28 | result.append(item.get(key, None)) 29 | return result 30 | 31 | 32 | def get_resource_ids(resource_type, module, theforeman, resource_names, search_field='name'): 33 | result = [] 34 | for i in range(0, len(resource_names)): 35 | try: 36 | resource = theforeman.search_resource(resource_type=resource_type, data={search_field: resource_names[i]}) 37 | if not resource: 38 | module.fail_json(msg='Could not find {type} {name}'.format( 39 | type=resource_type,name=resource_names[i])) 40 | result.append(resource.get('id')) 41 | except ForemanError as e: 42 | module.fail_json(msg='Search for {type} \'{name}\' throws an Error: {err}'.format( 43 | type=resource_type,name=resource_names[i],err=e.message)) 44 | return result 45 | 46 | def get_organization_ids(module, theforeman, organizations): 47 | return get_resource_ids(ORGANIZATIONS, module, theforeman, organizations) 48 | def get_location_ids(module, theforeman, locations): 49 | return get_resource_ids(LOCATIONS, module, theforeman, locations) 50 | def get_operatingsystem_ids(module, theforeman, operating_systems): 51 | return get_resource_ids(OPERATINGSYSTEMS, module, theforeman, operating_systems, search_field='title') 52 | 53 | 54 | def organizations_equal(data, resource): 55 | if 'organization_ids' in data: 56 | if not ('organizations' in resource): 57 | return False 58 | else: 59 | organization_ids = dict_list_to_list(resource['organizations'], 'id') 60 | if set(data['organization_ids']) != set(organization_ids): 61 | return False 62 | return True 63 | 64 | 65 | def locations_equal(data, resource): 66 | if 'location_ids' in data: 67 | if not ('locations' in resource): 68 | return False 69 | else: 70 | location_ids = dict_list_to_list(resource['locations'], 'id') 71 | if set(data['location_ids']) != set(location_ids): 72 | return False 73 | return True 74 | 75 | 76 | def operatingsystems_equal(data, resource): 77 | if 'operatingsystem_ids' in data: 78 | if not ('operatingsystems' in resource): 79 | return False 80 | else: 81 | operatingsystem_ids = dict_list_to_list(resource['operatingsystems'], 'id') 82 | if set(data['operatingsystem_ids']) != set(operatingsystem_ids): 83 | return False 84 | return True 85 | --------------------------------------------------------------------------------