-
Notifications
You must be signed in to change notification settings - Fork 1
/
netbox-proxmox-discover-vms.yml
238 lines (210 loc) · 12.5 KB
/
netbox-proxmox-discover-vms.yml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
- name: "NetBox-Proxmox VM Manager"
connection: local
hosts: localhost
gather_facts: False
vars:
proxmox_discovered_running_vms_configuration: []
proxmox_discovered_stopped_vms_configuration: []
proxmox_discovered_running_vms_networking: []
proxmox_discovered_running_vms_network_interface_mapping: []
proxmox_discovered_running_vms: []
proxmox_discovered_stopped_vms: []
proxmox_discovered_vms_configurations_raw: []
proxmox_vm_disk_names: {}
proxmox_vm_collected_disks: []
proxmox_vm_collected_disks_gb: []
proxmox_vm_collected_disks_mb: []
proxmox_vm_collected_disks_all: []
pm_to_nb_status_mapping:
'stopped': 'Offline'
'running': 'Active'
tasks:
- name: Collect secrets
include_vars:
file: secrets.yml
name: secrets_cfg
- name: Read Proxmox VM Configuration
include_vars:
file: vms.yml
name: proxmox_vm_cfg
- name: "Proxmox: Discover all VMs"
community.general.proxmox_vm_info:
api_host: "{{ secrets_cfg.proxmox.api_host | default(omit) }}"
api_user: "{{ secrets_cfg.proxmox.api_user | default(omit) }}"
api_token_id: "{{ secrets_cfg.proxmox.api_token_id | default(omit) }}"
api_token_secret: "{{ secrets_cfg.proxmox.api_token_secret | default(omit) }}"
config: current
register: proxmox_discovered_vms
- name: "Proxmox: Collect all running VMs (excluding templates)"
set_fact:
proxmox_discovered_running_vms: "{{ proxmox_discovered_running_vms | combine({item.name: item.vmid}) }}"
loop: "{{ proxmox_discovered_vms.proxmox_vms }}"
when: not item.template and item.status == "running"
- name: "Proxmox: Collect all stopped VMs (excluding templates)"
set_fact:
proxmox_discovered_stopped_vms: "{{ proxmox_discovered_stopped_vms | combine({item.name: item.vmid}) }}"
loop: "{{ proxmox_discovered_vms.proxmox_vms }}"
when: not item.template and item.status == "stopped"
- name: "Proxmox: Discover running VM configurations by vmid"
community.general.proxmox_vm_info:
api_host: "{{ secrets_cfg.proxmox.api_host | default(omit) }}"
api_user: "{{ secrets_cfg.proxmox.api_user | default(omit) }}"
api_token_id: "{{ secrets_cfg.proxmox.api_token_id | default(omit) }}"
api_token_secret: "{{ secrets_cfg.proxmox.api_token_secret | default(omit) }}"
vmid: "{{ proxmox_discovered_running_vms[item] }}"
network: true
config: current
with_items: "{{ proxmox_discovered_running_vms }}"
register: proxmox_discovered_vms_cfg
- name: "Proxmox: Collect Proxmox running VM configurations"
set_fact:
proxmox_discovered_running_vms_configuration: "{{ proxmox_discovered_running_vms_configuration + [ {'name': item.proxmox_vms[0].name, 'vmid': item.proxmox_vms[0].vmid, 'node': item.proxmox_vms[0].node, 'status': item.proxmox_vms[0].status, 'vcpus': item.proxmox_vms[0].cpus, 'memory': item.proxmox_vms[0].maxmem, 'disk': item.proxmox_vms[0].maxdisk, 'network': item.proxmox_vms[0].network} ] }}"
loop: "{{ proxmox_discovered_vms_cfg.results }}"
- name: "Proxmox: Discover stopped VM configurations by vmid"
community.general.proxmox_vm_info:
api_host: "{{ secrets_cfg.proxmox.api_host | default(omit) }}"
api_user: "{{ secrets_cfg.proxmox.api_user | default(omit) }}"
api_token_id: "{{ secrets_cfg.proxmox.api_token_id | default(omit) }}"
api_token_secret: "{{ secrets_cfg.proxmox.api_token_secret | default(omit) }}"
vmid: "{{ proxmox_discovered_stopped_vms[item] }}"
config: current
with_items: "{{ proxmox_discovered_stopped_vms }}"
register: proxmox_discovered_stopped_vms_cfg
- name: "Proxmox: Collect Proxmox stopped VM configurations"
set_fact:
proxmox_discovered_stopped_vms_configuration: "{{ proxmox_discovered_stopped_vms_configuration + [ {'name': item.proxmox_vms[0].name, 'vmid': item.proxmox_vms[0].vmid, 'node': item.proxmox_vms[0].node, 'status': item.proxmox_vms[0].status, 'vcpus': item.proxmox_vms[0].cpus, 'memory': item.proxmox_vms[0].maxmem, 'disk': item.proxmox_vms[0].maxdisk} ] }}"
loop: "{{ proxmox_discovered_stopped_vms_cfg.results }}"
- name: "Proxmox: Collect all networking information for VM(s)"
set_fact:
proxmox_discovered_running_vms_networking: "{{ proxmox_discovered_running_vms_networking + [ { 'name': item.0.name, 'interface': item.1.name, 'mac-address': item.1['hardware-address'], 'ip-addresses': item.1['ip-addresses'] } ] }}"
loop: "{{ proxmox_discovered_running_vms_configuration | subelements('network') }}"
when: item.1.name != "lo" and item.1.name != "docker0" and not item.1.name | regex_search('^br\-') # Exclude loopback and docker interface(s)
- name: "Proxmox: Collect network interface mappings for VM(s)"
set_fact:
proxmox_discovered_running_vms_network_interface_mapping: "{{ proxmox_discovered_running_vms_network_interface_mapping + [ { 'name': item.0.name, 'type': item.1['ip-address-type'], 'interface': item.0.interface, 'mac-address': item.0['mac-address'], 'ip': item.1['ip-address'] | string + '/' + item.1['prefix'] | string } ]}}"
loop: "{{ proxmox_discovered_running_vms_networking | subelements('ip-addresses') }}"
when: item.1['ip-address-type'] == "ipv4" # Only collect ipv4 addresses for this example
# Collect Proxmox VM disks
- name: "Proxmox: Discover VM (raw) disk configurations"
set_fact:
proxmox_discovered_vms_configurations_raw: "{{ proxmox_discovered_vms_configurations_raw + [ item.1.config | ansible.utils.keep_keys(target=['scsi', 'name'], matching_parameter= 'starts_with') ] }}"
loop: "{{ proxmox_discovered_vms_cfg.results | subelements('proxmox_vms') }}"
- name: Gather Proxmox disk names from collected output
set_fact:
proxmox_vm_disk_names: "{{ proxmox_vm_disk_names | combine({item.name: {'name': item.name, 'disks': item.keys() | map('regex_search', '^scsi[0-9]+') | select('string') | list}}) }}"
loop: "{{ proxmox_discovered_vms_configurations_raw }}"
- name: Categorize collected Proxmox disk names
set_fact:
proxmox_vm_collected_disks: "{{ proxmox_vm_collected_disks + [ {'name': item.0.name, 'disk_name': item.1, 'disk_config': (proxmox_discovered_vms_configurations_raw | selectattr('name', 'equalto', item.0.name) | first)[item.1]} ] }}"
loop: "{{ proxmox_vm_disk_names | subelements('disks') }}"
when: proxmox_discovered_vms_configurations_raw | selectattr('name', 'equalto', item.0.name)
- name: Categorize collected Proxmox disk names by disk size in GB
set_fact:
proxmox_vm_collected_disks_gb: "{{ proxmox_vm_collected_disks_gb + [ {'name': item.name, 'disk_name': item.disk_name, 'disk_size': item.disk_config | regex_replace('^.*,size=([0-9]+)G$', '\\1') } ] }}"
loop: "{{ proxmox_vm_collected_disks }}"
when: item.disk_config | regex_search(',size=\d+G$')
- name: Categorize collected Proxmox disk names by disk size in MB
set_fact:
proxmox_vm_collected_disks_mb: "{{ proxmox_vm_collected_disks_mb + [ {'name': item.name, 'disk_name': item.disk_name, 'disk_size': (item.disk_config | regex_replace('^.*,size=([0-9]+)M$', '\\1') | int / 1024) | int } ] }}"
loop: "{{ proxmox_vm_collected_disks }}"
when: item.disk_config | regex_search(',size=\d+M$')
- name: Merge collected Proxmox VM disk information
set_fact:
proxmox_vm_collected_disks_all: "{{ proxmox_vm_collected_disks_gb + proxmox_vm_collected_disks_mb }}"
- name: "NetBox: Add VM entries for running Proxmox VM(s)"
netbox.netbox.netbox_virtual_machine:
netbox_url: "{{ secrets_cfg.netbox.api_proto }}://{{ secrets_cfg.netbox.api_host }}:{{ secrets_cfg.netbox.api_port}}"
netbox_token: "{{ secrets_cfg.netbox.api_token }}"
data:
name: "{{ item.name }}"
status: "{{ pm_to_nb_status_mapping[item.status] }}"
tenant: "{{ vm_config[item.name].tenant if vm_config[item.name].tenant is defined else proxmox_vm_cfg.default_tenant }}"
virtual_machine_role: "{{ proxmox_vm_cfg.default_vm_device_role }}"
site: "{{ vm_config[item.name].site if vm_config[item.name].site is defined else proxmox_vm_cfg.default_site }}"
cluster: "{{ vm_config[item.name].vm_cluster if vm_config[item.name].vm_cluster is defined else proxmox_vm_cfg.default_vm_cluster }}"
vcpus: "{{ item.vcpus | int }}"
memory: "{{ (item.memory / (1024*1024)) | float | int }}"
custom_fields:
proxmox_vmid: "{{ item.vmid | int }}"
proxmox_node: "{{ item.node }}"
state: present
loop: "{{ proxmox_discovered_running_vms_configuration }}"
- name: "Proxmox: VM create interface(s)"
netbox.netbox.netbox_vm_interface:
netbox_url: "{{ secrets_cfg.netbox.api_proto }}://{{ secrets_cfg.netbox.api_host }}:{{ secrets_cfg.netbox.api_port}}"
netbox_token: "{{ secrets_cfg.netbox.api_token }}"
data:
virtual_machine: "{{ item.name }}"
name: "{{ item.interface }}"
mac_address: "{{ item['mac-address']}}"
state: present
with_items: "{{ proxmox_discovered_running_vms_network_interface_mapping }}"
- name: "Proxmox: VM assign IP address(es)"
netbox.netbox.netbox_ip_address:
netbox_url: "{{ secrets_cfg.netbox.api_proto }}://{{ secrets_cfg.netbox.api_host }}:{{ secrets_cfg.netbox.api_port}}"
netbox_token: "{{ secrets_cfg.netbox.api_token }}"
data:
address: "{{ item.ip }}"
status: Active
assigned_object:
virtual_machine: "{{ item.name }}"
name: "{{ item.interface }}"
state: present
with_items: "{{ proxmox_discovered_running_vms_network_interface_mapping }}"
- name: "NetBox: Update VM(s) primary_ip4"
netbox.netbox.netbox_virtual_machine:
netbox_url: "{{ secrets_cfg.netbox.api_proto }}://{{ secrets_cfg.netbox.api_host }}:{{ secrets_cfg.netbox.api_port}}"
netbox_token: "{{ secrets_cfg.netbox.api_token }}"
data:
name: "{{ item.name }}"
primary_ip4: "{{ item.config.ipconfig0.split(',')[0] | regex_replace('^ip=', '')}}"
state: present
loop: "{{ proxmox_discovered_vms.proxmox_vms }}"
when: not item.template and item.status == "running"
- name: "NetBox: Add VM entries for stopped Proxmox VM(s)"
netbox.netbox.netbox_virtual_machine:
netbox_url: "{{ secrets_cfg.netbox.api_proto }}://{{ secrets_cfg.netbox.api_host }}:{{ secrets_cfg.netbox.api_port}}"
netbox_token: "{{ secrets_cfg.netbox.api_token }}"
data:
name: "{{ item.name }}"
status: "{{ pm_to_nb_status_mapping[item.status] }}"
tenant: "{{ vm_config[item.name].tenant if vm_config[item.name].tenant is defined else proxmox_vm_cfg.default_tenant }}"
virtual_machine_role: "{{ proxmox_vm_cfg.default_vm_device_role }}"
site: "{{ vm_config[item.name].site if vm_config[item.name].site is defined else proxmox_vm_cfg.default_site }}"
cluster: "{{ vm_config[item.name].vm_cluster if vm_config[item.name].vm_cluster is defined else proxmox_vm_cfg.default_vm_cluster }}"
vcpus: "{{ item.vcpus | int }}"
memory: "{{ (item.memory / (1024*1024)) | float | int }}"
custom_fields:
proxmox_vmid: "{{ item.vmid | int }}"
proxmox_node: "{{ item.node }}"
state: present
loop: "{{ proxmox_discovered_stopped_vms_configuration }}"
- name: "NetBox: Add collected disks for Proxmox VMs"
netbox.netbox.netbox_virtual_disk:
netbox_url: "{{ secrets_cfg.netbox.api_proto }}://{{ secrets_cfg.netbox.api_host }}:{{ secrets_cfg.netbox.api_port}}"
netbox_token: "{{ secrets_cfg.netbox.api_token }}"
data:
virtual_machine: "{{ item.name }}"
name: "{{ item.disk_name }}"
size: "{{ item.disk_size }}"
state: present
loop: "{{ proxmox_vm_collected_disks_all }}"
- name: Find VMs in Netbox
local_action:
module: uri
url: "{{ secrets_cfg.netbox.api_proto }}://{{ secrets_cfg.netbox.api_host }}:{{ secrets_cfg.netbox.api_port}}/api/virtualization/virtual-machines/"
method: GET
headers:
Authorization: "Token {{ secrets_cfg.netbox.api_token }}"
status_code: 200
validate_certs: no
register: vms
- name: "NetBox: Remove VM(s) that no longer exist in Proxmox"
netbox.netbox.netbox_virtual_machine:
netbox_url: "{{ secrets_cfg.netbox.api_proto }}://{{ secrets_cfg.netbox.api_host }}:{{ secrets_cfg.netbox.api_port}}"
netbox_token: "{{ secrets_cfg.netbox.api_token }}"
data:
name: "{{ item.name }}"
state: absent
loop: "{{ vms.json.results }}"
when: (proxmox_discovered_running_vms[item.name] is not defined) and (proxmox_discovered_stopped_vms[item.name] is not defined)