summaryrefslogtreecommitdiffstats
path: root/ansible/deployment_poc
diff options
context:
space:
mode:
authorGeorg2022-01-05 23:59:13 +0100
committerGeorg2022-01-05 23:59:13 +0100
commit7bb88aaa661dc6756e862562c47368de11657f44 (patch)
tree545d6f1d86c1d42ed04d558450996c54fdca631d /ansible/deployment_poc
parent0e53f4d766e95205b20951fd252f6118ce05352b (diff)
downloadsystem-7bb88aaa661dc6756e862562c47368de11657f44.tar.gz
system-7bb88aaa661dc6756e862562c47368de11657f44.tar.bz2
system-7bb88aaa661dc6756e862562c47368de11657f44.zip
Init Ansible NetBox DHCP POC deployment
Signed-off-by: Georg <georg@lysergic.dev>
Diffstat (limited to 'ansible/deployment_poc')
-rw-r--r--ansible/deployment_poc/README.md1
-rw-r--r--ansible/deployment_poc/flow.svg321
-rw-r--r--ansible/deployment_poc/playbooks/deploy.yml104
-rw-r--r--ansible/deployment_poc/tasks/configure_dhcp.yml31
-rw-r--r--ansible/deployment_poc/tasks/configure_libvirt.yml61
-rw-r--r--ansible/deployment_poc/tasks/init_dhcp.yml7
-rw-r--r--ansible/deployment_poc/tasks/netbox_evaluate_cluster.yml50
-rw-r--r--ansible/deployment_poc/tasks/netbox_evaluate_ip.yml5
-rw-r--r--ansible/deployment_poc/tasks/netbox_evaluate_prefix.yml6
-rw-r--r--ansible/deployment_poc/tasks/netbox_evaluate_site.yml5
-rw-r--r--ansible/deployment_poc/tasks/netbox_evaluate_vm.yml23
-rw-r--r--ansible/deployment_poc/tasks/netbox_query_cluster.yml14
-rw-r--r--ansible/deployment_poc/tasks/netbox_query_ip.yml14
-rw-r--r--ansible/deployment_poc/tasks/netbox_query_prefix.yml14
-rw-r--r--ansible/deployment_poc/tasks/netbox_query_site.yml14
-rw-r--r--ansible/deployment_poc/tasks/netbox_query_vm.yml15
-rw-r--r--ansible/deployment_poc/tasks/netbox_tags_post.yml24
-rw-r--r--ansible/deployment_poc/tasks/netbox_tags_pre.yml34
-rw-r--r--ansible/deployment_poc/templates/dhcpd.conf.j25
-rw-r--r--ansible/deployment_poc/templates/libvirt-storage-template.xml.j216
-rw-r--r--ansible/deployment_poc/templates/libvirt-template.xml.j2174
-rw-r--r--ansible/deployment_poc/variables/deploy-variables.yml8
22 files changed, 946 insertions, 0 deletions
diff --git a/ansible/deployment_poc/README.md b/ansible/deployment_poc/README.md
new file mode 100644
index 0000000..bfdd87e
--- /dev/null
+++ b/ansible/deployment_poc/README.md
@@ -0,0 +1 @@
+![Flowchart about the deployment and provisioning process](flow.svg)
diff --git a/ansible/deployment_poc/flow.svg b/ansible/deployment_poc/flow.svg
new file mode 100644
index 0000000..d791ff2
--- /dev/null
+++ b/ansible/deployment_poc/flow.svg
@@ -0,0 +1,321 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.0//EN" "http://www.w3.org/TR/2001/PR-SVG-20010719/DTD/svg10.dtd">
+<svg width="106cm" height="76cm" viewBox="-561 -1021 2120 1505" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
+ <g>
+ <path style="fill: #ffffff" d="M -478.5 0 L -152.5,0 C -107.489,0 -71,28.8855 -71,64.5177 C -71,100.15 -107.489,129.035 -152.5,129.035 L -478.5,129.035 C -523.511,129.035 -560,100.15 -560,64.5177 C -560,28.8855 -523.511,0 -478.5,0z"/>
+ <path style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #000000" d="M -478.5 0 L -152.5,0 C -107.489,0 -71,28.8855 -71,64.5177 C -71,100.15 -107.489,129.035 -152.5,129.035 L -478.5,129.035 C -523.511,129.035 -560,100.15 -560,64.5177 C -560,28.8855 -523.511,0 -478.5,0"/>
+ <text font-size="12.8" style="fill: #000000;text-anchor:middle;font-family:sans-serif;font-style:normal;font-weight:normal" x="-315.5" y="52.3989">
+ <tspan x="-315.5" y="52.3989">START</tspan>
+ <tspan x="-315.5" y="68.3989"></tspan>
+ <tspan x="-315.5" y="84.3989">"User decides to provision a new virtual machine"</tspan>
+ </text>
+ </g>
+ <g>
+ <path style="fill: #ffffff" d="M 40 237.333 L 313.55,180 L 313.55,323.333 L 40,323.333 L 40,237.333z"/>
+ <path style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #000000" d="M 40 237.333 L 313.55,180 L 313.55,323.333 L 40,323.333 L 40,237.333"/>
+ <text font-size="12.8" style="fill: #000000;text-anchor:middle;font-family:sans-serif;font-style:normal;font-weight:normal" x="176.775" y="252.215">
+ <tspan x="176.775" y="252.215">NetBox</tspan>
+ <tspan x="176.775" y="268.215">(User)</tspan>
+ <tspan x="176.775" y="284.215"></tspan>
+ <tspan x="176.775" y="300.215">1. User creates a "Virtual Machine" object</tspan>
+ <tspan x="176.775" y="316.215">and enters the desired specifications</tspan>
+ </text>
+ </g>
+ <g>
+ <line style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #000000" x1="-144.538" y1="129.513" x2="93.0249" y2="219.827"/>
+ <polygon style="fill: #000000" points="100.035,222.492 88.9113,223.613 93.0249,219.827 92.4649,214.265 "/>
+ <polygon style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #000000" points="100.035,222.492 88.9113,223.613 93.0249,219.827 92.4649,214.265 "/>
+ </g>
+ <g>
+ <rect style="fill: #ffffff" x="880" y="240" width="349.4" height="70"/>
+ <rect style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #000000" x="880" y="240" width="349.4" height="70"/>
+ <text font-size="12.8" style="fill: #000000;text-anchor:middle;font-family:sans-serif;font-style:normal;font-weight:normal" x="1054.7" y="262.881">
+ <tspan x="1054.7" y="262.881">Webhook</tspan>
+ <tspan x="1054.7" y="278.881"></tspan>
+ <tspan x="1054.7" y="294.881">3. HTTPS POST is received and body data is parsed</tspan>
+ </text>
+ </g>
+ <g>
+ <polygon style="fill: #ffffff" points="897.125,360 1157.24,360 1120.12,462 860,462 "/>
+ <polygon style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #000000" points="897.125,360 1157.24,360 1120.12,462 860,462 "/>
+ <text font-size="12.8" style="fill: #000000;text-anchor:middle;font-family:sans-serif;font-style:normal;font-weight:normal" x="1008.62" y="382.881">
+ <tspan x="1008.62" y="382.881">NetBox</tspan>
+ <tspan x="1008.62" y="398.881">(System)</tspan>
+ <tspan x="1008.62" y="414.881"></tspan>
+ <tspan x="1008.62" y="430.881">2. System creates a JSON object</tspan>
+ <tspan x="1008.62" y="446.881">and sends it out via HTTPS POST</tspan>
+ </text>
+ </g>
+ <g>
+ <polygon style="fill: #ffffff" points="1248.77,40 1557.74,40 1508.96,174 1200,174 "/>
+ <polygon style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #000000" points="1248.77,40 1557.74,40 1508.96,174 1200,174 "/>
+ <text font-size="12.8" style="fill: #000000;text-anchor:middle;font-family:sans-serif;font-style:normal;font-weight:normal" x="1378.87" y="62.8812">
+ <tspan x="1378.87" y="62.8812">YES</tspan>
+ <tspan x="1378.87" y="78.8812"></tspan>
+ <tspan x="1378.87" y="94.8812">Wehook</tspan>
+ <tspan x="1378.87" y="110.881">(System)</tspan>
+ <tspan x="1378.87" y="126.881"></tspan>
+ <tspan x="1378.87" y="142.881">4. A shell script is executed, initiating</tspan>
+ <tspan x="1378.87" y="158.881">a SSH session</tspan>
+ </text>
+ </g>
+ <g>
+ <polygon style="fill: #ffffff" points="770.22,40 1000.44,108.904 770.22,177.808 540,108.904 "/>
+ <polygon style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #000000" points="770.22,40 1000.44,108.904 770.22,177.808 540,108.904 "/>
+ <text font-size="12.8" style="fill: #000000;text-anchor:middle;font-family:sans-serif;font-style:normal;font-weight:normal" x="770.22" y="104.785">
+ <tspan x="770.22" y="104.785">Does the received object contain valid JSON</tspan>
+ <tspan x="770.22" y="120.785">with the required attributes?</tspan>
+ </text>
+ </g>
+ <g>
+ <polygon style="fill: #ffffff" points="607.212,340 774.424,411.25 607.212,482.5 440,411.25 "/>
+ <polygon style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #000000" points="607.212,340 774.424,411.25 607.212,482.5 440,411.25 "/>
+ <text font-size="12.8" style="fill: #000000;text-anchor:middle;font-family:sans-serif;font-style:normal;font-weight:normal" x="607.212" y="407.131">
+ <tspan x="607.212" y="407.131">Does the created object contain</tspan>
+ <tspan x="607.212" y="423.131">the requireed fields?</tspan>
+ </text>
+ </g>
+ <g>
+ <line style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #000000" x1="314.551" y1="302.747" x2="507.445" y2="374.262"/>
+ <polygon style="fill: #000000" points="514.478,376.869 503.363,378.081 507.445,374.262 506.839,368.705 "/>
+ <polygon style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #000000" points="514.478,376.869 503.363,378.081 507.445,374.262 506.839,368.705 "/>
+ </g>
+ <g>
+ <line style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #000000" x1="775.424" y1="411.145" x2="868.251" y2="411.087"/>
+ <polygon style="fill: #000000" points="875.751,411.083 865.754,416.089 868.251,411.087 865.748,406.089 "/>
+ <polygon style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #000000" points="875.751,411.083 865.754,416.089 868.251,411.087 865.748,406.089 "/>
+ </g>
+ <g>
+ <line style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #000000" x1="1026.24" y1="358.996" x2="1039.38" y2="320.222"/>
+ <polygon style="fill: #000000" points="1041.78,313.118 1043.31,324.194 1039.38,320.222 1033.84,320.985 "/>
+ <polygon style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #000000" points="1041.78,313.118 1043.31,324.194 1039.38,320.222 1033.84,320.985 "/>
+ </g>
+ <g>
+ <line style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #000000" x1="578.953" y1="351.04" x2="350.273" y2="-136.195"/>
+ <polygon style="fill: #000000" points="347.086,-142.984 355.861,-136.056 350.273,-136.195 346.809,-131.807 "/>
+ <polygon style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #000000" points="347.086,-142.984 355.861,-136.056 350.273,-136.195 346.809,-131.807 "/>
+ </g>
+ <g>
+ <line style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #000000" x1="993.303" y1="239.153" x2="857.778" y2="160.026"/>
+ <polygon style="fill: #000000" points="851.301,156.244 862.458,156.968 857.778,160.026 857.416,165.604 "/>
+ <polygon style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #000000" points="851.301,156.244 862.458,156.968 857.778,160.026 857.416,165.604 "/>
+ </g>
+ <g>
+ <line style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #000000" x1="1001.38" y1="108.181" x2="1213.84" y2="107.516"/>
+ <polygon style="fill: #000000" points="1221.34,107.493 1211.36,112.524 1213.84,107.516 1211.32,102.524 "/>
+ <polygon style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #000000" points="1221.34,107.493 1211.36,112.524 1213.84,107.516 1211.32,102.524 "/>
+ </g>
+ <g>
+ <line style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #000000" x1="700.682" y1="59.8147" x2="418.504" y2="-139.385"/>
+ <polygon style="fill: #000000" points="412.377,-143.711 423.43,-142.028 418.504,-139.385 417.663,-133.859 "/>
+ <polygon style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #000000" points="412.377,-143.711 423.43,-142.028 418.504,-139.385 417.663,-133.859 "/>
+ </g>
+ <g>
+ <rect style="fill: #ffffff" x="1260" y="-240" width="286.601" height="134.793"/>
+ <rect style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #000000" x="1260" y="-240" width="286.601" height="134.793"/>
+ <text font-size="12.8" style="fill: #000000;text-anchor:middle;font-family:sans-serif;font-style:normal;font-weight:normal" x="1403.3" y="-192.722">
+ <tspan x="1403.3" y="-192.722">Ansible</tspan>
+ <tspan x="1403.3" y="-176.722">(System)</tspan>
+ <tspan x="1403.3" y="-160.722"></tspan>
+ <tspan x="1403.3" y="-144.722">5. A playbook is executed</tspan>
+ </text>
+ </g>
+ <g>
+ <line style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #000000" x1="1384.81" y1="39.002" x2="1396.48" y2="-94.5735"/>
+ <polygon style="fill: #000000" points="1397.14,-102.045 1401.25,-91.6477 1396.48,-94.5735 1391.28,-92.5182 "/>
+ <polygon style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #000000" points="1397.14,-102.045 1401.25,-91.6477 1396.48,-94.5735 1391.28,-92.5182 "/>
+ </g>
+ <g>
+ <path style="fill: #ffffff" d="M 1260 -530.92 C 1312.89,-552.73 1339.34,-560 1392.22,-560 C 1445.12,-560 1471.56,-552.73 1524.45,-530.92 L 1524.45,-414.6 C 1471.56,-392.791 1445.12,-385.521 1392.22,-385.521 C 1339.34,-385.521 1312.89,-392.791 1260,-414.6 L 1260,-530.92z"/>
+ <path style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #000000" d="M 1260 -530.92 C 1312.89,-552.73 1339.34,-560 1392.22,-560 C 1445.12,-560 1471.56,-552.73 1524.45,-530.92 L 1524.45,-414.6 C 1471.56,-392.791 1445.12,-385.521 1392.22,-385.521 C 1339.34,-385.521 1312.89,-392.791 1260,-414.6 L 1260,-530.92"/>
+ <path style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #000000" d="M 1260 -530.92 C 1312.89,-509.11 1339.34,-501.84 1392.22,-501.84 C 1445.12,-501.84 1471.56,-509.11 1524.45,-530.92"/>
+ <text font-size="12.8" style="fill: #000000;text-anchor:middle;font-family:sans-serif;font-style:normal;font-weight:normal" x="1392.22" y="-478.339">
+ <tspan x="1392.22" y="-478.339">NetBox</tspan>
+ <tspan x="1392.22" y="-462.339">(System)</tspan>
+ <tspan x="1392.22" y="-446.339"></tspan>
+ <tspan x="1392.22" y="-430.339">6. The Virtual Machine object is queried </tspan>
+ </text>
+ </g>
+ <g>
+ <line style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #000000" x1="1400.78" y1="-240.992" x2="1395.84" y2="-374.856"/>
+ <polygon style="fill: #000000" points="1395.56,-382.351 1400.93,-372.542 1395.84,-374.856 1390.93,-372.173 "/>
+ <polygon style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #000000" points="1395.56,-382.351 1400.93,-372.542 1395.84,-374.856 1390.93,-372.173 "/>
+ </g>
+ <g>
+ <polygon style="fill: #ffffff" points="918.547,-200 1177.09,-97.7588 918.547,4.48232 660,-97.7588 "/>
+ <polygon style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #000000" points="918.547,-200 1177.09,-97.7588 918.547,4.48232 660,-97.7588 "/>
+ <text font-size="12.8" style="fill: #000000;text-anchor:middle;font-family:sans-serif;font-style:normal;font-weight:normal" x="918.547" y="-109.878">
+ <tspan x="918.547" y="-109.878">Does the Virtual Machine object contain the required</tspan>
+ <tspan x="918.547" y="-93.8776">fields, is it in the correct state and</tspan>
+ <tspan x="918.547" y="-77.8776">compliant?l</tspan>
+ </text>
+ </g>
+ <g>
+ <line style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #000000" x1="1298.76" y1="-398.763" x2="1013.15" y2="-172.656"/>
+ <polygon style="fill: #000000" points="1007.27,-168 1012.01,-178.128 1013.15,-172.656 1018.22,-170.287 "/>
+ <polygon style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #000000" points="1007.27,-168 1012.01,-178.128 1013.15,-172.656 1018.22,-170.287 "/>
+ </g>
+ <g>
+ <rect style="fill: #ffffff" x="120" y="-280" width="388.45" height="134"/>
+ <rect style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #000000" x="120" y="-280" width="388.45" height="134"/>
+ <text font-size="12.8" style="fill: #000000;text-anchor:middle;font-family:sans-serif;font-style:normal;font-weight:normal" x="314.225" y="-241.119">
+ <tspan x="314.225" y="-241.119">NO</tspan>
+ <tspan x="314.225" y="-225.119"></tspan>
+ <tspan x="314.225" y="-209.119">(System)</tspan>
+ <tspan x="314.225" y="-193.119"></tspan>
+ <tspan x="314.225" y="-177.119">Received data is discarded, and the process is aborted</tspan>
+ </text>
+ </g>
+ <g>
+ <line style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #000000" x1="742.533" y1="-131.324" x2="519.002" y2="-173.95"/>
+ <polygon style="fill: #000000" points="511.635,-175.355 522.394,-178.393 519.002,-173.95 520.521,-168.57 "/>
+ <polygon style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #000000" points="511.635,-175.355 522.394,-178.393 519.002,-173.95 520.521,-168.57 "/>
+ </g>
+ <g>
+ <rect style="fill: #ffffff" x="800" y="-420" width="252" height="134.793"/>
+ <rect style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #000000" x="800" y="-420" width="252" height="134.793"/>
+ <text font-size="12.8" style="fill: #000000;text-anchor:middle;font-family:sans-serif;font-style:normal;font-weight:normal" x="926" y="-380.722">
+ <tspan x="926" y="-380.722">Ansible</tspan>
+ <tspan x="926" y="-364.722">(System)</tspan>
+ <tspan x="926" y="-348.722"></tspan>
+ <tspan x="926" y="-332.722">7. A virtual hard disk is created</tspan>
+ <tspan x="926" y="-316.722">he actual virtual machine is defined</tspan>
+ </text>
+ </g>
+ <g>
+ <rect style="fill: #ffffff" x="800" y="-580" width="230.15" height="86"/>
+ <rect style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #000000" x="800" y="-580" width="230.15" height="86"/>
+ <text font-size="12.8" style="fill: #000000;text-anchor:middle;font-family:sans-serif;font-style:normal;font-weight:normal" x="915.075" y="-557.119">
+ <tspan x="915.075" y="-557.119">Ansible</tspan>
+ <tspan x="915.075" y="-541.119">(System)</tspan>
+ <tspan x="915.075" y="-525.119"></tspan>
+ <tspan x="915.075" y="-509.119">8. The virtual machine is started</tspan>
+ </text>
+ </g>
+ <g>
+ <rect style="fill: #ffffff" x="774.367" y="-709.01" width="289.3" height="86"/>
+ <rect style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #000000" x="774.367" y="-709.01" width="289.3" height="86"/>
+ <text font-size="12.8" style="fill: #000000;text-anchor:middle;font-family:sans-serif;font-style:normal;font-weight:normal" x="919.017" y="-686.129">
+ <tspan x="919.017" y="-686.129">Libvirt</tspan>
+ <tspan x="919.017" y="-670.129">(System)</tspan>
+ <tspan x="919.017" y="-654.129"></tspan>
+ <tspan x="919.017" y="-638.129">9. The virtual machine is network booted </tspan>
+ </text>
+ </g>
+ <g>
+ <rect style="fill: #ffffff" x="780" y="-860" width="280.125" height="83.646"/>
+ <rect style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #000000" x="780" y="-860" width="280.125" height="83.646"/>
+ <line style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #000000" x1="808.012" y1="-860" x2="808.012" y2="-776.354"/>
+ <line style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #000000" x1="1032.11" y1="-860" x2="1032.11" y2="-776.354"/>
+ <text font-size="12.8" style="fill: #000000;text-anchor:middle;font-family:sans-serif;font-style:normal;font-weight:normal" x="920.062" y="-822.296">
+ <tspan x="920.062" y="-822.296">The DHCP/TFTP/NFS process</tspan>
+ <tspan x="920.062" y="-806.296">loads a network operating system</tspan>
+ </text>
+ </g>
+ <g>
+ <rect style="fill: #ffffff" x="780" y="-1020" width="252.6" height="86"/>
+ <rect style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #000000" x="780" y="-1020" width="252.6" height="86"/>
+ <text font-size="12.8" style="fill: #000000;text-anchor:middle;font-family:sans-serif;font-style:normal;font-weight:normal" x="906.3" y="-997.119">
+ <tspan x="906.3" y="-997.119">OpenSUSE</tspan>
+ <tspan x="906.3" y="-981.119">(System)</tspan>
+ <tspan x="906.3" y="-965.119"></tspan>
+ <tspan x="906.3" y="-949.119">11. The installer initializes the disk </tspan>
+ </text>
+ </g>
+ <g>
+ <line style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #000000" x1="921.948" y1="-420.998" x2="918.257" y2="-483.286"/>
+ <polygon style="fill: #000000" points="917.814,-490.773 923.396,-481.087 918.257,-483.286 913.414,-480.495 "/>
+ <polygon style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #000000" points="917.814,-490.773 923.396,-481.087 918.257,-483.286 913.414,-480.495 "/>
+ </g>
+ <g>
+ <line style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #000000" x1="916.419" y1="-580.985" x2="917.376" y2="-612.293"/>
+ <polygon style="fill: #000000" points="917.605,-619.79 922.297,-609.642 917.376,-612.293 912.302,-609.947 "/>
+ <polygon style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #000000" points="917.605,-619.79 922.297,-609.642 917.376,-612.293 912.302,-609.947 "/>
+ </g>
+ <g>
+ <line style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #000000" x1="919.319" y1="-710.005" x2="919.701" y2="-765.626"/>
+ <polygon style="fill: #000000" points="919.753,-773.125 924.684,-763.091 919.701,-765.626 914.684,-763.16 "/>
+ <polygon style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #000000" points="919.753,-773.125 924.684,-763.091 919.701,-765.626 914.684,-763.16 "/>
+ </g>
+ <g>
+ <rect style="fill: #ffffff" x="1180" y="-1020" width="352.65" height="102"/>
+ <rect style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #000000" x="1180" y="-1020" width="352.65" height="102"/>
+ <text font-size="12.8" style="fill: #000000;text-anchor:middle;font-family:sans-serif;font-style:normal;font-weight:normal" x="1356.32" y="-997.119">
+ <tspan x="1356.32" y="-997.119">OpenSUSE</tspan>
+ <tspan x="1356.32" y="-981.119">(System)</tspan>
+ <tspan x="1356.32" y="-965.119"></tspan>
+ <tspan x="1356.32" y="-949.119">10. Requested oftware specifications</tspan>
+ <tspan x="1356.32" y="-933.119">are collected </tspan>
+ </text>
+ </g>
+ <g>
+ <line style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #000000" x1="1179.11" y1="-972.15" x2="1043.33" y2="-974.564"/>
+ <polygon style="fill: #000000" points="1035.83,-974.697 1045.92,-979.519 1043.33,-974.564 1045.74,-969.52 "/>
+ <polygon style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #000000" points="1035.83,-974.697 1045.92,-979.519 1043.33,-974.564 1045.74,-969.52 "/>
+ </g>
+ <g>
+ <line style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #000000" x1="1043.88" y1="-860.983" x2="1196.71" y2="-913.817"/>
+ <polygon style="fill: #000000" points="1203.79,-916.267 1195.98,-908.274 1196.71,-913.817 1192.71,-917.726 "/>
+ <polygon style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #000000" points="1203.79,-916.267 1195.98,-908.274 1196.71,-913.817 1192.71,-917.726 "/>
+ </g>
+ <g>
+ <rect style="fill: #ffffff" x="220" y="-1020" width="352.65" height="102"/>
+ <rect style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #000000" x="220" y="-1020" width="352.65" height="102"/>
+ <text font-size="12.8" style="fill: #000000;text-anchor:middle;font-family:sans-serif;font-style:normal;font-weight:normal" x="396.325" y="-989.119">
+ <tspan x="396.325" y="-989.119">OpenSUSE</tspan>
+ <tspan x="396.325" y="-973.119">(System)</tspan>
+ <tspan x="396.325" y="-957.119"></tspan>
+ <tspan x="396.325" y="-941.119">12. The operating system is installed </tspan>
+ </text>
+ </g>
+ <g>
+ <line style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #000000" x1="779.024" y1="-975.003" x2="583.371" y2="-971.934"/>
+ <polygon style="fill: #000000" points="575.872,-971.817 585.793,-976.973 583.371,-971.934 585.95,-966.974 "/>
+ <polygon style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #000000" points="575.872,-971.817 585.793,-976.973 583.371,-971.934 585.95,-966.974 "/>
+ </g>
+ <g>
+ <line style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #000000" x1="921.532" y1="-199.812" x2="923.715" y2="-274.478"/>
+ <polygon style="fill: #000000" points="923.935,-281.975 928.64,-271.833 923.715,-274.478 918.644,-272.126 "/>
+ <polygon style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #000000" points="923.935,-281.975 928.64,-271.833 923.715,-274.478 918.644,-272.126 "/>
+ </g>
+ <g>
+ <line style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #000000" x1="1360.09" y1="-917.011" x2="1385.15" y2="-570.617"/>
+ <polygon style="fill: #000000" points="1385.69,-563.136 1379.98,-572.75 1385.15,-570.617 1389.95,-573.471 "/>
+ <polygon style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #000000" points="1385.69,-563.136 1379.98,-572.75 1385.15,-570.617 1389.95,-573.471 "/>
+ </g>
+ <g>
+ <rect style="fill: #ffffff" x="-320" y="-1020" width="352.65" height="102"/>
+ <rect style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #000000" x="-320" y="-1020" width="352.65" height="102"/>
+ <text font-size="12.8" style="fill: #000000;text-anchor:middle;font-family:sans-serif;font-style:normal;font-weight:normal" x="-143.675" y="-997.119">
+ <tspan x="-143.675" y="-997.119">OpenSUSE</tspan>
+ <tspan x="-143.675" y="-981.119">(System)</tspan>
+ <tspan x="-143.675" y="-965.119"></tspan>
+ <tspan x="-143.675" y="-949.119">13. The system starts base daemons</tspan>
+ <tspan x="-143.675" y="-933.119">and sends a report via emaill</tspan>
+ </text>
+ </g>
+ <g>
+ <line style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #000000" x1="218.997" y1="-969" x2="43.3886" y2="-969"/>
+ <polygon style="fill: #000000" points="35.8886,-969 45.8886,-974 43.3886,-969 45.8886,-964 "/>
+ <polygon style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #000000" points="35.8886,-969 45.8886,-974 43.3886,-969 45.8886,-964 "/>
+ </g>
+ <g>
+ <path style="fill: #ffffff" d="M -478.625 -480 L -153.125,-480 C -108.183,-480 -71.75,-451.114 -71.75,-415.482 C -71.75,-379.85 -108.183,-350.965 -153.125,-350.965 L -478.625,-350.965 C -523.567,-350.965 -560,-379.85 -560,-415.482 C -560,-451.114 -523.567,-480 -478.625,-480z"/>
+ <path style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #000000" d="M -478.625 -480 L -153.125,-480 C -108.183,-480 -71.75,-451.114 -71.75,-415.482 C -71.75,-379.85 -108.183,-350.965 -153.125,-350.965 L -478.625,-350.965 C -523.567,-350.965 -560,-379.85 -560,-415.482 C -560,-451.114 -523.567,-480 -478.625,-480"/>
+ <text font-size="12.8" style="fill: #000000;text-anchor:middle;font-family:sans-serif;font-style:normal;font-weight:normal" x="-315.875" y="-427.601">
+ <tspan x="-315.875" y="-427.601">END</tspan>
+ <tspan x="-315.875" y="-411.601"></tspan>
+ <tspan x="-315.875" y="-395.601">Pipeline completed</tspan>
+ </text>
+ </g>
+ <g>
+ <line style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #000000" x1="-159.853" y1="-916.998" x2="-292.601" y2="-490.295"/>
+ <polygon style="fill: #000000" points="-294.829,-483.133 -296.632,-494.167 -292.601,-490.295 -287.084,-491.196 "/>
+ <polygon style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #000000" points="-294.829,-483.133 -296.632,-494.167 -292.601,-490.295 -287.084,-491.196 "/>
+ </g>
+ <g>
+ <line style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #000000" x1="119.001" y1="-275.735" x2="-115.526" y2="-351.1"/>
+ <polygon style="fill: #000000" points="-122.666,-353.395 -111.616,-355.096 -115.526,-351.1 -114.676,-345.575 "/>
+ <polygon style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #000000" points="-122.666,-353.395 -111.616,-355.096 -115.526,-351.1 -114.676,-345.575 "/>
+ </g>
+</svg>
diff --git a/ansible/deployment_poc/playbooks/deploy.yml b/ansible/deployment_poc/playbooks/deploy.yml
new file mode 100644
index 0000000..858ed00
--- /dev/null
+++ b/ansible/deployment_poc/playbooks/deploy.yml
@@ -0,0 +1,104 @@
+---
+- hosts: status_planned
+ gather_facts: no
+ vars:
+ token: "{{ nb_token }}"
+ vm_name: "{{ inventory_hostname }}"
+ tag_merged: []
+ debug_merged: []
+ vars_files:
+ - ../variables/deploy-variables.yml
+
+ pre_tasks:
+ - name: Check lock
+ wait_for:
+ path: "{{ lockfile }}"
+ state: absent
+ timeout: 600
+ msg: Lock did not disappear in time
+ delegate_to: localhost
+
+ - name: Create lock
+ file:
+ path: "{{ lockfile }}"
+ state: touch
+ delegate_to: localhost
+
+ tasks:
+ - name: Pipeline
+ block:
+ - name: Gather details
+ block:
+ - import_tasks: "../tasks/netbox_query_vm.yml"
+ - import_tasks: "../tasks/netbox_query_cluster.yml"
+ no_log: true
+
+ - name: Assign variables
+ block:
+ - import_tasks: "../tasks/netbox_evaluate_cluster.yml"
+ - import_tasks: "../tasks/netbox_evaluate_vm.yml"
+
+ - name: Verify compliance
+ block:
+ - name: Check status
+ fail:
+ msg: The object is not Planned.
+ when: status != 'planned'
+
+ - name: Check tag
+ fail:
+ msg: The object is marked as already being in deployment.
+ when: '"active-deployment" in tags'
+
+ - name: Check platform
+ fail:
+ msg: The object does not contain a valid platform attribute.
+ when: os != 'openSUSE-Leap-x86_64'
+
+ - name: Write tag and journal
+ import_tasks: "../tasks/netbox_tags_pre.yml"
+
+ - name: Gather site configuration
+ block:
+ - import_tasks: "../tasks/netbox_query_site.yml"
+ - import_tasks: "../tasks/netbox_evaluate_site.yml"
+ no_log: true
+
+ - name: Gather prefix
+ block:
+ - import_tasks: "../tasks/netbox_query_prefix.yml"
+ - import_tasks: "../tasks/netbox_evaluate_prefix.yml"
+ no_log: true
+
+ - name: Gather IP address
+ block:
+ - import_tasks: "../tasks/netbox_query_ip.yml"
+ - import_tasks: "../tasks/netbox_evaluate_ip.yml"
+ no_log: true
+
+ - name: Provision virtual machine
+ import_tasks: "../tasks/configure_libvirt.yml"
+
+ - name: Configure DHCP
+ import_tasks: "../tasks/init_dhcp.yml"
+
+
+# - name: Prepare unattended installation (TO-DO if needed - not needed if script inside autoinst.xml fetches values itself)
+# block:
+# - import_tasks: "../tasks/prepare_platform_{{ os }}.yml"
+
+
+ always:
+ - name: Restore original tags
+ import_tasks: "../tasks/netbox_tags_post.yml"
+
+ - name: Remove lock
+ file:
+ path: "{{ lockfile }}"
+ state: absent
+ delegate_to: localhost
+
+ - name: Debug
+ ansible.builtin.debug:
+ msg: "{{ status if status is defined}} - {{ tags if tags is defined }} - {{ host if host is defined }} - {{ host_status if host_status is defined }} - {{ os if os is defined }} - {{ vcpus if vcpus is defined }} - {{ memory if memory is defined }} - {{ disk if disk is defined }}"
+
diff --git a/ansible/deployment_poc/tasks/configure_dhcp.yml b/ansible/deployment_poc/tasks/configure_dhcp.yml
new file mode 100644
index 0000000..9802b0e
--- /dev/null
+++ b/ansible/deployment_poc/tasks/configure_dhcp.yml
@@ -0,0 +1,31 @@
+---
+- name: Configure DHCP
+ block:
+ - name: Query DHCP server
+ set_fact:
+ dhcp_os: "{{ hostvars[dhcp_host]['platforms'][0] }}"
+
+ - name: Insert DHCP host block
+ ansible.builtin.blockinfile:
+ #backup: yes
+ block: "{{ lookup('template', '../templates/dhcpd.conf.j2') }}"
+ marker: "### {mark} Ansible managed block for {{ vm_name }} ###"
+ path: "/etc/dhcpd.conf"
+ #delegate_to: "{{ dhcp_host }}"
+ become: yes
+ become_method: doas
+ when: dhcp_os == 'openbsd-x86_64'
+
+ - name: Insert DHCP static mapping
+ vyos.vyos.vyos_config:
+ backup: yes
+ backup_options:
+ dir_path: "/tmp/"
+ comment: "Configured as part of {{ vm_name }} deployment"
+ lines:
+ - "set service dhcp-server shared-network-name LAN subnet {{ prefix_display }} static-mapping {{ vm_name }} mac-address {{ mac_address }}"
+ - "set service dhcp-server shared-network-name LAN subnet {{ prefix_display }} static-mapping {{ vm_name }} ip-address {{ ip_address }}"
+ save: no # CHANGE BEFORE ROLLOUT
+ when: dhcp_os == 'vyos-x86_64'
+ delegate_to: "{{ dhcp_host }}"
+
diff --git a/ansible/deployment_poc/tasks/configure_libvirt.yml b/ansible/deployment_poc/tasks/configure_libvirt.yml
new file mode 100644
index 0000000..b3e49b6
--- /dev/null
+++ b/ansible/deployment_poc/tasks/configure_libvirt.yml
@@ -0,0 +1,61 @@
+---
+- name: Provision VM
+ block:
+ - name: Create domain template
+ ansible.builtin.template:
+ src: "../templates/libvirt-template.xml.j2"
+ dest: "../templates/libvirt-{{ inventory_hostname }}.xml"
+ group: lysergic
+ mode: '0660'
+
+ - name: Create storage template
+ ansible.builtin.template:
+ src: "../templates/libvirt-storage-template.xml.j2"
+ dest: "../templates/generated/libvirt-storage-{{ inventory_hostname }}.xml"
+ group: lysergic
+ mode: '0660'
+
+ - name: Define domain
+ virt:
+ uri: "{{ libvirt_url }}"
+ command: define
+ xml: "{{ lookup('template', '../templates/libvirt-template.xml.j2') }}"
+ autostart: no
+ # delegate_to: localhost
+
+ - name: Query volumes
+ ansible.builtin.command:
+ argv:
+ - /usr/bin/virsh
+ - -c
+ - "{{ libvirt_url }}"
+ - vol-list
+ - "{{ storage.name }}"
+ register: volumes
+ no_log: true
+
+ - name: Define volume
+ ansible.builtin.command:
+ argv:
+ - /usr/bin/virsh
+ - -c
+ - "{{ libvirt_url }}"
+ - vol-create
+ - "{{ storage.name }}"
+ - "../templates/generated/libvirt-storage-{{ inventory_hostname }}.xml"
+ when: vm_name not in volumes.stdout
+
+ - name: Fetch MAC address
+ ansible.builtin.shell: "/usr/bin/virsh -c {{ libvirt_url }} domiflist {{ vm_name }} | awk '{print $5}' | cut -d/ -f 1 | tail -n 2 | head -n1" # ewww :-(
+ register: domiflist_mac
+
+ - name: Store MAC address
+ set_fact:
+ mac_address: "{{ domiflist_mac.stdout }}"
+
+ delegate_to: localhost
+
+ always:
+ - name: Debug
+ ansible.builtin.debug:
+ msg: "{{ libvirt_url if libvirt_url is defined }} - {{ storage.name if storage is defined }} - {{ mac_address if mac_address is defined }}"
diff --git a/ansible/deployment_poc/tasks/init_dhcp.yml b/ansible/deployment_poc/tasks/init_dhcp.yml
new file mode 100644
index 0000000..fbd4765
--- /dev/null
+++ b/ansible/deployment_poc/tasks/init_dhcp.yml
@@ -0,0 +1,7 @@
+---
+- name: Initialize DHCP configurator
+ include_tasks: "../tasks/configure_dhcp.yml"
+ vars:
+ dhcp_host: "{{ item }}"
+ with_items: "{{ dhcp_servers }}"
+
diff --git a/ansible/deployment_poc/tasks/netbox_evaluate_cluster.yml b/ansible/deployment_poc/tasks/netbox_evaluate_cluster.yml
new file mode 100644
index 0000000..1e8b07f
--- /dev/null
+++ b/ansible/deployment_poc/tasks/netbox_evaluate_cluster.yml
@@ -0,0 +1,50 @@
+---
+- name: Evaluate cluster
+ block:
+ - name: Increment counters
+ set_fact:
+ retry_count: "{{ 0 if retry_count is undefined else retry_count | int +1 }}"
+ host_count: "{{ 0 if retry_count is undefined else host_count | int +1 }}"
+
+ - name: Pick cluster host
+ set_fact:
+ #host_choice: "{{ nb_hosts.json.results[nb_hosts.json.count | random | int] }}" #PICK RANDOM
+ #host_choice: "{{ nb_hosts.json.results[1] }}" #FAIL TEST
+ host_choice: "{{ nb_hosts.json.results[host_count | int] }}" #INCREMENT
+ no_log: true
+
+ - name: Evaluate cluster host status
+ set_fact:
+ host_status: "{{ host_choice.status.value }}"
+ #register: host_status
+
+ - name: Evaluate cluster host name
+ set_fact:
+ host: "{{ host_choice.name }}"
+
+ - name: Evaluate cluster host status
+ fail:
+ msg: Host is not ready.
+ when: host_status != 'active'
+
+ - name: Evaluate cluster host configuration
+ set_fact:
+ storage: "{{ host_choice.config_context.storage[0] }}"
+ #deployment_servers: "{{ host_choice.config_context.deployment_servers }}"
+ dhcp_servers: "{{ host_choice.config_context.dhcp_servers }}"
+ dns_servers: "{{ host_choice.config_context.dns_servers }}"
+ when: host_status == 'active'
+
+ rescue:
+ - name: Check retry counter
+ fail:
+ msg: "Too many retries - no host is ready"
+ when: retry_count | int == 3 and host_status != 'active'
+
+ - debug:
+ msg: "{{ host if host is defined }} - {{ host_status if host_status is defined }}"
+
+ - name: Re-evaluate cluster
+ include_tasks: "../tasks/netbox_evaluate_cluster.yml"
+ when: host_status != 'active'
+
diff --git a/ansible/deployment_poc/tasks/netbox_evaluate_ip.yml b/ansible/deployment_poc/tasks/netbox_evaluate_ip.yml
new file mode 100644
index 0000000..828b15e
--- /dev/null
+++ b/ansible/deployment_poc/tasks/netbox_evaluate_ip.yml
@@ -0,0 +1,5 @@
+---
+- name: Define IP address
+ set_fact:
+ ip_address: "{{ nb_ip.json[0].address | ansible.netcommon.ipaddr('address') }}"
+
diff --git a/ansible/deployment_poc/tasks/netbox_evaluate_prefix.yml b/ansible/deployment_poc/tasks/netbox_evaluate_prefix.yml
new file mode 100644
index 0000000..74983e4
--- /dev/null
+++ b/ansible/deployment_poc/tasks/netbox_evaluate_prefix.yml
@@ -0,0 +1,6 @@
+---
+- name: Evaluate prefix options
+ set_fact:
+ prefix_id: "{{ nb_prefix.json.results[0].id }}"
+ prefix_display: "{{ nb_prefix.json.results[0].display }}"
+
diff --git a/ansible/deployment_poc/tasks/netbox_evaluate_site.yml b/ansible/deployment_poc/tasks/netbox_evaluate_site.yml
new file mode 100644
index 0000000..abd5347
--- /dev/null
+++ b/ansible/deployment_poc/tasks/netbox_evaluate_site.yml
@@ -0,0 +1,5 @@
+---
+- name: Gather site configuration
+ set_fact:
+ site_id: "{{ nb_site.json.results[0].id }}"
+
diff --git a/ansible/deployment_poc/tasks/netbox_evaluate_vm.yml b/ansible/deployment_poc/tasks/netbox_evaluate_vm.yml
new file mode 100644
index 0000000..8188024
--- /dev/null
+++ b/ansible/deployment_poc/tasks/netbox_evaluate_vm.yml
@@ -0,0 +1,23 @@
+---
+- name: Pick hard- and software
+ # not needed, can be pulled from hostvars
+ set_fact:
+ vcpus: "{{ nb_vm.json.results[0].vcpus | int }}"
+ os: "{{ nb_vm.json.results[0].platform.name }}"
+
+# - name: Pick virtual hardware specifications
+# # not needed, part of hostvars
+# set_fact:
+# memory: "{{ nb_vm.json.results[0].memory }}"
+# disk: "{{ nb_vm.json.results[0].disk }}"
+
+- name: Pick metadata
+ set_fact:
+ id: "{{ nb_vm.json.results[0].id }}"
+ site: "{{ hostvars[inventory_hostname]['sites'][0] }}"
+ status: "{{ nb_vm.json.results[0].status.value }}"
+
+# # not needed, part of hostvars
+# #tags: "{{ nb_vm.json.results[0].tags[0].slug }}"
+# #tags: "{{ nb_vm.json.results[0].tags | sum(start=[]) | map(attribute='slug') }}"
+
diff --git a/ansible/deployment_poc/tasks/netbox_query_cluster.yml b/ansible/deployment_poc/tasks/netbox_query_cluster.yml
new file mode 100644
index 0000000..1f948d1
--- /dev/null
+++ b/ansible/deployment_poc/tasks/netbox_query_cluster.yml
@@ -0,0 +1,14 @@
+---
+- name: Locate cluster hosts
+ ansible.builtin.uri:
+ url: "{{ endpoint }}/dcim/devices/?cluster_id={{ nb_vm.json.results[0].cluster.id }}"
+ client_cert: "{{ cert }}"
+ client_key: "{{ key }}"
+ method: GET
+ return_content: yes
+ headers:
+ Accept: application/json
+ Authorization: "Token {{ token }}"
+ register: nb_hosts
+ delegate_to: localhost
+
diff --git a/ansible/deployment_poc/tasks/netbox_query_ip.yml b/ansible/deployment_poc/tasks/netbox_query_ip.yml
new file mode 100644
index 0000000..f0ed7b7
--- /dev/null
+++ b/ansible/deployment_poc/tasks/netbox_query_ip.yml
@@ -0,0 +1,14 @@
+---
+- name: Query available address
+ ansible.builtin.uri:
+ url: "{{ endpoint }}/ipam/prefixes/{{ prefix_id }}/available-ips/?limit=1"
+ client_cert: "{{ cert }}"
+ client_key: "{{ key }}"
+ method: GET
+ return_content: yes
+ headers:
+ Accept: application/json
+ Authorization: "Token {{ token }}"
+ register: nb_ip
+ delegate_to: localhost
+
diff --git a/ansible/deployment_poc/tasks/netbox_query_prefix.yml b/ansible/deployment_poc/tasks/netbox_query_prefix.yml
new file mode 100644
index 0000000..d0c0990
--- /dev/null
+++ b/ansible/deployment_poc/tasks/netbox_query_prefix.yml
@@ -0,0 +1,14 @@
+---
+- name: Query prefix
+ ansible.builtin.uri:
+ url: "{{ endpoint }}/ipam/prefixes/?site_id={{ site_id }}&tenant={{ tenant }}&limit=1"
+ client_cert: "{{ cert }}"
+ client_key: "{{ key }}"
+ method: GET
+ return_content: yes
+ headers:
+ Accept: application/json
+ Authorization: "Token {{ token }}"
+ register: nb_prefix
+ delegate_to: localhost
+
diff --git a/ansible/deployment_poc/tasks/netbox_query_site.yml b/ansible/deployment_poc/tasks/netbox_query_site.yml
new file mode 100644
index 0000000..5894a6c
--- /dev/null
+++ b/ansible/deployment_poc/tasks/netbox_query_site.yml
@@ -0,0 +1,14 @@
+---
+- name: Query site
+ ansible.builtin.uri:
+ url: "{{ endpoint }}/dcim/sites/?slug={{ site }}"
+ client_cert: "{{ cert }}"
+ client_key: "{{ key }}"
+ method: GET
+ return_content: yes
+ headers:
+ Accept: application/json
+ Authorization: "Token {{ token }}"
+ register: nb_site
+ delegate_to: localhost
+
diff --git a/ansible/deployment_poc/tasks/netbox_query_vm.yml b/ansible/deployment_poc/tasks/netbox_query_vm.yml
new file mode 100644
index 0000000..52308f0
--- /dev/null
+++ b/ansible/deployment_poc/tasks/netbox_query_vm.yml
@@ -0,0 +1,15 @@
+---
+ # consider ditching this block, would need to work around missing cluster ID in hostvars
+- name: Query VM
+ ansible.builtin.uri:
+ url: "{{ endpoint }}/virtualization/virtual-machines/?name={{ inventory_hostname }}"
+ client_cert: "{{ cert }}"
+ client_key: "{{ key }}"
+ method: GET
+ return_content: yes
+ headers:
+ Accept: application/json
+ Authorization: "Token {{ token }}"
+ register: nb_vm
+ delegate_to: localhost
+
diff --git a/ansible/deployment_poc/tasks/netbox_tags_post.yml b/ansible/deployment_poc/tasks/netbox_tags_post.yml
new file mode 100644
index 0000000..9b24ca3
--- /dev/null
+++ b/ansible/deployment_poc/tasks/netbox_tags_post.yml
@@ -0,0 +1,24 @@
+---
+- name: Post-deployment tagging
+ block:
+ - name: Construct body for tagging
+ set_fact:
+ body2: ' {% for tag in tag_exist %}{% if loop.last %}{"slug": "{{ tag }}"}{% else %}{"slug": "{{ tag }}"},{% endif %}{% endfor %}'
+ when: tag_exist is defined
+
+ - name: Set post-deployment tags
+ ansible.builtin.uri:
+ url: "{{ endpoint }}/virtualization/virtual-machines/{{ id }}/"
+ client_cert: "{{ cert }}"
+ client_key: "{{ key }}"
+ method: PATCH
+ return_content: yes
+ headers:
+ Accept: application/json
+ Authorization: "Token {{ token }}"
+ body_format: json
+ body: ' {"tags": [ {{ body2 }}]}'
+ delegate_to: localhost
+ when: body2 is defined
+ no_log: true
+
diff --git a/ansible/deployment_poc/tasks/netbox_tags_pre.yml b/ansible/deployment_poc/tasks/netbox_tags_pre.yml
new file mode 100644
index 0000000..23a804b
--- /dev/null
+++ b/ansible/deployment_poc/tasks/netbox_tags_pre.yml
@@ -0,0 +1,34 @@
+---
+- name: Pre-deployment tagging
+ block:
+ - name: Gather tags
+ set_fact:
+ tag_exist: "{{ tags }}"
+ tag_append: "['active-deployment']"
+
+ - name: Merge tags
+ set_fact:
+ tag_merged: "{{ tag_merged + [item] }}"
+ with_items:
+ - "{{ tag_exist }}"
+ - "{{ tag_append }}"
+
+ - name: Construct body for tagging
+ set_fact:
+ body1: ' {% for tag in tag_merged %}{% if loop.last %}{"slug": "{{ tag }}"}{% else %}{"slug": "{{ tag }}"},{% endif %}{% endfor %}'
+
+ - name: Set pre-deployment tags
+ ansible.builtin.uri:
+ url: "{{ endpoint }}/virtualization/virtual-machines/{{ id }}/"
+ client_cert: "{{ cert }}"
+ client_key: "{{ key }}"
+ method: PATCH
+ return_content: yes
+ headers:
+ Accept: application/json
+ Authorization: "Token {{ token }}"
+ body_format: json
+ body: ' {"tags": [ {{ body1 }}]}'
+ delegate_to: localhost
+ no_log: true
+
diff --git a/ansible/deployment_poc/templates/dhcpd.conf.j2 b/ansible/deployment_poc/templates/dhcpd.conf.j2
new file mode 100644
index 0000000..5309ae4
--- /dev/null
+++ b/ansible/deployment_poc/templates/dhcpd.conf.j2
@@ -0,0 +1,5 @@
+host {{ vm_name }} {
+ hardware ethernet {{ mac_address }};
+ fixed-address {{ ip_address }};
+ filename "replace-with-bootfile";
+}
diff --git a/ansible/deployment_poc/templates/libvirt-storage-template.xml.j2 b/ansible/deployment_poc/templates/libvirt-storage-template.xml.j2
new file mode 100644
index 0000000..9ce3ff1
--- /dev/null
+++ b/ansible/deployment_poc/templates/libvirt-storage-template.xml.j2
@@ -0,0 +1,16 @@
+<volume type='file'>
+ <name>{{ inventory_hostname }}_root_disk.qcow2</name>
+ <source>
+ </source>
+ <capacity unit='GB'>{{ disk }}</capacity>
+ <target>
+ <path>{{ storage.name }}</path>
+ <format type='qcow2'/>
+ <permissions>
+ <mode>0660</mode>
+ <owner>107</owner>
+ <group>107</group>
+ </permissions>
+ </target>
+</volume>
+
diff --git a/ansible/deployment_poc/templates/libvirt-template.xml.j2 b/ansible/deployment_poc/templates/libvirt-template.xml.j2
new file mode 100644
index 0000000..8c4170d
--- /dev/null
+++ b/ansible/deployment_poc/templates/libvirt-template.xml.j2
@@ -0,0 +1,174 @@
+<domain type='kvm'>
+ <name>{{ inventory_hostname }}</name>
+ <metadata>
+ <libosinfo:libosinfo xmlns:libosinfo="http://libosinfo.org/xmlns/libvirt/domain/1.0">
+ </libosinfo:libosinfo>
+ </metadata>
+ <memory unit='MB'>{{ memory }}</memory>
+ <currentMemory unit='GB'>{{ memory }}</currentMemory>
+ <vcpu placement='static'>{{ vcpus }}</vcpu>
+ <resource>
+ <partition>/machine</partition>
+ </resource>
+ <os>
+ <type arch='x86_64' machine='pc-q35-5.2'>hvm</type>
+ <loader readonly='yes' type='pflash'>/usr/share/qemu/ovmf-x86_64-code.bin</loader>
+ <nvram>/var/lib/libvirt/qemu/nvram/{{ inventory_hostname }}_VARS.fd</nvram>
+ <bootmenu enable='no'/>
+ </os>
+ <features>
+ <acpi/>
+ <apic/>
+ <vmport state='off'/>
+ </features>
+ <cpu mode='custom' match='exact' check='full'>
+ <model fallback='forbid'>Broadwell-IBRS</model>
+ <vendor>Intel</vendor>
+ <feature policy='require' name='vme'/>
+ <feature policy='require' name='ss'/>
+ <feature policy='require' name='vmx'/>
+ <feature policy='require' name='f16c'/>
+ <feature policy='require' name='rdrand'/>
+ <feature policy='require' name='hypervisor'/>
+ <feature policy='require' name='arat'/>
+ <feature policy='require' name='tsc_adjust'/>
+ <feature policy='require' name='umip'/>
+ <feature policy='require' name='md-clear'/>
+ <feature policy='require' name='stibp'/>
+ <feature policy='require' name='arch-capabilities'/>
+ <feature policy='require' name='ssbd'/>
+ <feature policy='require' name='xsaveopt'/>
+ <feature policy='require' name='pdpe1gb'/>
+ <feature policy='require' name='abm'/>
+ <feature policy='require' name='skip-l1dfl-vmentry'/>
+ <feature policy='require' name='pschange-mc-no'/>
+ </cpu>
+ <clock offset='utc'>
+ <timer name='rtc' tickpolicy='catchup'/>
+ <timer name='pit' tickpolicy='delay'/>
+ <timer name='hpet' present='no'/>
+ </clock>
+ <on_poweroff>destroy</on_poweroff>
+ <on_reboot>restart</on_reboot>
+ <on_crash>destroy</on_crash>
+ <pm>
+ <suspend-to-mem enabled='no'/>
+ <suspend-to-disk enabled='no'/>
+ </pm>
+ <devices>
+ <emulator>/usr/bin/qemu-system-x86_64</emulator>
+ <!--disk type='file' device='disk'>
+ <driver name='qemu' type='qcow2'/>
+ <source file='/mnt/arr1-store1/vmstore1/{{ inventory_hostname }}_root_disk.qcow2' index='2'/>
+ <backingStore/>
+ <target dev='vda' bus='virtio'/>
+ <boot order='1'/>
+ <alias name='virtio-disk0'/>
+ <address type='pci' domain='0x0000' bus='0x04' slot='0x00' function='0x0'/>
+ </disk-->
+ <!--disk type='file' device='cdrom'>
+ <driver name='qemu'/>
+ <source file='/mnt/iso/openSUSE-Leap-15.3-NET-x86_64.iso'/>
+ <target dev='sda' bus='sata'/>
+ <readonly/>
+ <boot order='2'/>
+ <alias name='sata0-0-0'/>
+ <address type='drive' controller='0' bus='0' target='0' unit='0'/>
+ </disk-->
+ <controller type='usb' index='0' model='qemu-xhci' ports='15'>
+ <alias name='usb'/>
+ <address type='pci' domain='0x0000' bus='0x02' slot='0x00' function='0x0'/>
+ </controller>
+ <controller type='sata' index='0'>
+ <alias name='ide'/>
+ <address type='pci' domain='0x0000' bus='0x00' slot='0x1f' function='0x2'/>
+ </controller>
+ <controller type='pci' index='0' model='pcie-root'>
+ <alias name='pcie.0'/>
+ </controller>
+ <controller type='pci' index='1' model='pcie-root-port'>
+ <model name='pcie-root-port'/>
+ <target chassis='1' port='0x8'/>
+ <alias name='pci.1'/>
+ <address type='pci' domain='0x0000' bus='0x00' slot='0x01' function='0x0' multifunction='on'/>
+ </controller>
+ <controller type='pci' index='2' model='pcie-root-port'>
+ <model name='pcie-root-port'/>
+ <target chassis='2' port='0x9'/>
+ <alias name='pci.2'/>
+ <address type='pci' domain='0x0000' bus='0x00' slot='0x01' function='0x1'/>
+ </controller>
+ <controller type='pci' index='3' model='pcie-root-port'>
+ <model name='pcie-root-port'/>
+ <target chassis='3' port='0xa'/>
+ <alias name='pci.3'/>
+ <address type='pci' domain='0x0000' bus='0x00' slot='0x01' function='0x2'/>
+ </controller>
+ <controller type='pci' index='4' model='pcie-root-port'>
+ <model name='pcie-root-port'/>
+ <target chassis='4' port='0xb'/>
+ <alias name='pci.4'/>
+ <address type='pci' domain='0x0000' bus='0x00' slot='0x01' function='0x3'/>
+ </controller>
+ <controller type='pci' index='5' model='pcie-root-port'>
+ <model name='pcie-root-port'/>
+ <target chassis='5' port='0xc'/>
+ <alias name='pci.5'/>
+ <address type='pci' domain='0x0000' bus='0x00' slot='0x01' function='0x4'/>
+ </controller>
+ <controller type='pci' index='6' model='pcie-root-port'>
+ <model name='pcie-root-port'/>
+ <target chassis='6' port='0xd'/>
+ <alias name='pci.6'/>
+ <address type='pci' domain='0x0000' bus='0x00' slot='0x01' function='0x5'/>
+ </controller>
+ <controller type='pci' index='7' model='pcie-root-port'>
+ <model name='pcie-root-port'/>
+ <target chassis='7' port='0xe'/>
+ <alias name='pci.7'/>
+ <address type='pci' domain='0x0000' bus='0x00' slot='0x01' function='0x6'/>
+ </controller>
+ <controller type='virtio-serial' index='0'>
+ <alias name='virtio-serial0'/>
+ <address type='pci' domain='0x0000' bus='0x03' slot='0x00' function='0x0'/>
+ </controller>
+ <interface type='network'>
+ <source network='LAN01'/>
+ <model type='virtio'/>
+ <address type='pci' domain='0x0000' bus='0x01' slot='0x00' function='0x0'/>
+ </interface>
+ <serial type='pty'>
+ <source path='/dev/pts/4'/>
+ <target type='isa-serial' port='0'>
+ <model name='isa-serial'/>
+ </target>
+ <alias name='serial0'/>
+ </serial>
+ <console type='pty' tty='/dev/pts/4'>
+ <source path='/dev/pts/4'/>
+ <target type='serial' port='0'/>
+ <alias name='serial0'/>
+ </console>
+ <channel type='unix'>
+ <target type='virtio' name='org.qemu.guest_agent.0' state='connected'/>
+ <alias name='channel0'/>
+ <address type='virtio-serial' controller='0' bus='0' port='1'/>
+ </channel>
+ <input type='mouse' bus='ps2'>
+ <alias name='input0'/>
+ </input>
+ <input type='keyboard' bus='ps2'>
+ <alias name='input1'/>
+ </input>
+ <memballoon model='virtio'>
+ <alias name='balloon0'/>
+ <address type='pci' domain='0x0000' bus='0x05' slot='0x00' function='0x0'/>
+ </memballoon>
+ <rng model='virtio'>
+ <backend model='random'>/dev/urandom</backend>
+ <alias name='rng0'/>
+ <address type='pci' domain='0x0000' bus='0x06' slot='0x00' function='0x0'/>
+ </rng>
+ </devices>
+</domain>
+
diff --git a/ansible/deployment_poc/variables/deploy-variables.yml b/ansible/deployment_poc/variables/deploy-variables.yml
new file mode 100644
index 0000000..fbb4cc1
--- /dev/null
+++ b/ansible/deployment_poc/variables/deploy-variables.yml
@@ -0,0 +1,8 @@
+---
+lockfile: "../locks/deploy.lock"
+endpoint: ""
+cert: ""
+key: ""
+nb_token:
+libvirt_url: "qemu+tls://{{ host }}/system"
+tenant: ""