Diversi aggiustamenti: buona parte delle richieste utente ora sono chiavi di configurazione, richiesti al new. Wrapper per trovare gli IP dopo il clone, ma necessario verificare funzionamento.
This commit is contained in:
parent
26e4cb28f2
commit
529dfe9be2
@ -6,6 +6,59 @@ runtime: python
|
|||||||
template:
|
template:
|
||||||
description: ProxmoxVE provider, Ansible powered with Python
|
description: ProxmoxVE provider, Ansible powered with Python
|
||||||
config:
|
config:
|
||||||
password:
|
pveHostname:
|
||||||
description: Password of the root user over PVE.
|
type: String
|
||||||
|
default: pve.vavassori.lcl
|
||||||
|
description: PVE hostname
|
||||||
|
pveNodeName:
|
||||||
|
type: String
|
||||||
|
default: pve
|
||||||
|
description: Node of the Proxmox VE to which write the VM.
|
||||||
|
pveVerifySSL:
|
||||||
|
type: Number
|
||||||
|
default: 0
|
||||||
|
description: Verify SSL certificate of PVE Node
|
||||||
|
pveTokenName:
|
||||||
|
type: String
|
||||||
|
default: root@pam!automata
|
||||||
|
description: Authentication token for PVE Node
|
||||||
|
pveTokenValue:
|
||||||
|
type: String
|
||||||
secret: true
|
secret: true
|
||||||
|
description: Authentication token value for PVE Node
|
||||||
|
pveDefaultStorage:
|
||||||
|
type: String
|
||||||
|
default: local-lvm
|
||||||
|
description: Default storage in which put the VMs
|
||||||
|
hostname:
|
||||||
|
type: String
|
||||||
|
description: Hostname of the new machine
|
||||||
|
vmTemplate:
|
||||||
|
type: String
|
||||||
|
default: debian12
|
||||||
|
description: OS name of the needed template
|
||||||
|
vmSockets:
|
||||||
|
type: Number
|
||||||
|
default: 1
|
||||||
|
description: Number of sockets of the new VMs
|
||||||
|
vmCores:
|
||||||
|
type: Number
|
||||||
|
default: 1
|
||||||
|
description: Number of cores per socket of the new VM
|
||||||
|
vmRAM:
|
||||||
|
type: Number
|
||||||
|
default: 2048
|
||||||
|
description: Number of MBs of RAM of the new VM
|
||||||
|
vmDiskSize:
|
||||||
|
type: Number
|
||||||
|
default: 20
|
||||||
|
description: Number of GBs of the VM disk
|
||||||
|
cloudUsername:
|
||||||
|
type: String
|
||||||
|
default: syntaxerrormmm
|
||||||
|
description: Username created in the cloud-init phase
|
||||||
|
cloudPassword:
|
||||||
|
type: String
|
||||||
|
default: cicciopasticcio
|
||||||
|
secret: true
|
||||||
|
description: Password of the username created in the cloud-init phase.
|
||||||
|
@ -3,50 +3,59 @@
|
|||||||
import pulumi
|
import pulumi
|
||||||
import pulumi_proxmoxve as proxmox
|
import pulumi_proxmoxve as proxmox
|
||||||
import pulumi_command as command
|
import pulumi_command as command
|
||||||
import yaml
|
from pvewrapper import PveWrapper
|
||||||
from dotmap import DotMap
|
import additional_config
|
||||||
|
|
||||||
# Import configuration parameters from input.yaml
|
|
||||||
input_ = DotMap(yaml.safe_load(open("./input.yaml", "r").read()))
|
|
||||||
|
|
||||||
# Check pulumi configuration
|
# Check pulumi configuration
|
||||||
config = pulumi.Config()
|
config = pulumi.Config()
|
||||||
|
|
||||||
provider = proxmox.Provider("proxmoxve",
|
username = config.require("pveTokenName").split("!")[0]
|
||||||
endpoint=input_.pve.url,
|
tokenname = config.require("pveTokenName").split("!")[1]
|
||||||
insecure=input_.pve.insecure,
|
pvehostname = config.require("pveHostname")
|
||||||
username=input_.pve.username,
|
pvenodename = config.require("pveNodeName")
|
||||||
password=config.require_secret("password")
|
pveURL=f"https://{pvehostname}:8006/"
|
||||||
|
verify = True if config.get_int("pveVerifySSL") == 1 else False
|
||||||
|
pvedefaultstorage = config.require("pveDefaultStorage")
|
||||||
|
vm_name: str = config.require("hostname")
|
||||||
|
vm_username: str = config.require("cloudUsername")
|
||||||
|
|
||||||
|
provider = proxmox.Provider(pvehostname,
|
||||||
|
endpoint=pveURL,
|
||||||
|
insecure=not verify,
|
||||||
|
api_token=config.require_secret("pveTokenValue").apply(lambda x: f"{username}!{tokenname}={x}")
|
||||||
)
|
)
|
||||||
|
|
||||||
vm_name = input_.vm.name
|
pve = PveWrapper(pvehostname,
|
||||||
vm_username = input_.vm.username
|
username=username,
|
||||||
ansible_playbook = input_.ansible_playbook
|
token_name=tokenname,
|
||||||
|
token_value=config.require_secret("pveTokenValue").apply(lambda v: f"{v}"),
|
||||||
|
nodename=pvenodename,
|
||||||
|
verify_ssl=verify
|
||||||
|
)
|
||||||
|
|
||||||
vm = proxmox.vm.VirtualMachine("vm",
|
vm = proxmox.vm.VirtualMachine(vm_name,
|
||||||
name = vm_name,
|
name = vm_name,
|
||||||
node_name = input_.pve.nodename,
|
node_name = pvenodename,
|
||||||
agent = proxmox.vm.VirtualMachineAgentArgs(
|
agent = proxmox.vm.VirtualMachineAgentArgs(
|
||||||
enabled = True,
|
enabled = True,
|
||||||
trim = True,
|
trim = True,
|
||||||
type = "virtio"
|
type = "virtio"
|
||||||
),
|
),
|
||||||
bios = "seabios",
|
|
||||||
cpu = proxmox.vm.VirtualMachineCpuArgs(
|
cpu = proxmox.vm.VirtualMachineCpuArgs(
|
||||||
cores = input_.vm.cores,
|
cores = config.get_int("vmSockets"),
|
||||||
sockets = input_.vm.sockets
|
sockets = config.get_int("vmCores")
|
||||||
),
|
),
|
||||||
memory = proxmox.vm.VirtualMachineMemoryArgs(dedicated = input_.vm.ram),
|
memory = proxmox.vm.VirtualMachineMemoryArgs(dedicated = config.get_int("vmRAM")),
|
||||||
clone = proxmox.vm.VirtualMachineCloneArgs(
|
clone = proxmox.vm.VirtualMachineCloneArgs(
|
||||||
node_name = input_.pve.nodename,
|
node_name = pvenodename,
|
||||||
vm_id = input_.vm.clone,
|
vm_id = additional_config.os_to_template[config.require("vmTemplate")],
|
||||||
full = True
|
full = False
|
||||||
),
|
),
|
||||||
disks = [
|
disks = [
|
||||||
proxmox.vm.VirtualMachineDiskArgs(
|
proxmox.vm.VirtualMachineDiskArgs(
|
||||||
interface = "virtio0",
|
interface = "virtio0",
|
||||||
datastore_id = input_.pve.storage,
|
datastore_id = pvedefaultstorage,
|
||||||
size = input_.vm.disksize,
|
size = config.get_int("vmDiskSize"),
|
||||||
file_format = "raw"
|
file_format = "raw"
|
||||||
)
|
)
|
||||||
],
|
],
|
||||||
@ -56,17 +65,16 @@ vm = proxmox.vm.VirtualMachine("vm",
|
|||||||
model = "virtio"
|
model = "virtio"
|
||||||
)
|
)
|
||||||
],
|
],
|
||||||
on_boot = True,
|
|
||||||
operating_system = proxmox.vm.VirtualMachineOperatingSystemArgs(
|
operating_system = proxmox.vm.VirtualMachineOperatingSystemArgs(
|
||||||
type = "l26"
|
type = "l26"
|
||||||
),
|
),
|
||||||
initialization = proxmox.vm.VirtualMachineInitializationArgs(
|
initialization = proxmox.vm.VirtualMachineInitializationArgs(
|
||||||
type = "nocloud",
|
type = "nocloud",
|
||||||
datastore_id = input_.pve.storage,
|
datastore_id = pvedefaultstorage,
|
||||||
user_account = proxmox.vm.VirtualMachineInitializationUserAccountArgs(
|
user_account = proxmox.vm.VirtualMachineInitializationUserAccountArgs(
|
||||||
username = vm_username,
|
username = vm_username,
|
||||||
password = input_.vm.password,
|
password = config.require_secret("cloudPassword"),
|
||||||
keys = input_.vm.sshkeys
|
keys = additional_config.sshkeys
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
opts = pulumi.ResourceOptions(
|
opts = pulumi.ResourceOptions(
|
||||||
@ -75,27 +83,27 @@ vm = proxmox.vm.VirtualMachine("vm",
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
# First item of the ipv4_addresses is of the loopback interface (so the usual 127.0.0.1). Let's get the second and grab only the text.
|
ipv4_addresses = vm.vm_id.apply(lambda vm_id: pve.ipv4_addresses(f"{vm_id}"))
|
||||||
|
|
||||||
# Creating the inventory file
|
# Creating the inventory file
|
||||||
inventory = command.local.Command("a-inventory",
|
inventory = command.local.Command("a-inventory",
|
||||||
create = vm.ipv4_addresses[1][0].apply(
|
create = vm.ipv4_addresses.apply(lambda ipaddr:
|
||||||
lambda ipaddr: f"echo '{vm_name} ansible_host={ipaddr} ansible_user={vm_username}' >./inventory"
|
f"echo '{vm_name} ansible_host={ipaddr[0]['ipv4_address']} ansible_user={vm_username}' >./inventory"
|
||||||
),
|
),
|
||||||
delete = "rm -f ./inventory",
|
delete = "rm -f ./inventory",
|
||||||
opts = pulumi.ResourceOptions(depends_on = [ vm ])
|
opts = pulumi.ResourceOptions(depends_on = [ ipv4_addresses ])
|
||||||
)
|
)
|
||||||
|
|
||||||
# Try the deployment with ansible
|
|
||||||
|
|
||||||
# Applying the command
|
# Applying the command
|
||||||
execute_ansible = command.local.Command("a-deploy",
|
for playbook in additional_config.playbooks:
|
||||||
create = vm.ipv4_addresses[1][0].apply(
|
shortname: str = playbook.split(".")[0]
|
||||||
lambda ipaddr: f"ansible-playbook {ansible_playbook}"
|
command.local.Command(f"ap-{shortname}",
|
||||||
|
create = vm.ipv4_addresses.apply(
|
||||||
|
lambda run: f"ansible-playbook {run[0]['ipv4_address']}"
|
||||||
),
|
),
|
||||||
delete = "rm -f ./ansible.log",
|
delete = "rm -f ./ansible.log",
|
||||||
opts = pulumi.ResourceOptions(depends_on = [ inventory ])
|
opts = pulumi.ResourceOptions(depends_on = [ inventory ])
|
||||||
)
|
)
|
||||||
|
|
||||||
# Outputs
|
# Outputs
|
||||||
pulumi.export("ip", vm.ipv4_addresses[1][0])
|
pulumi.export("ipv4_addresses", vm.ipv4_addresses)
|
||||||
|
21
pvm-ansible/additional_config.py
Normal file
21
pvm-ansible/additional_config.py
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
# encoding: utf-8
|
||||||
|
|
||||||
|
sshkeys = [
|
||||||
|
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIFioHkaV1NhX6NCqsJakJw8EVBOcDHm1MEbpY499CPtG syntaxerrormmm@fisso",
|
||||||
|
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAILu91hBh8pNRt4eE1pug0Y4jCHZDCcMJ+vj3CiF5EQHV syntaxerrormmm@syntaxxps",
|
||||||
|
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAILH5q/ObtC4VhNT88gebezP/svpvCoQLoZCh4DvUn4xq syntaxerrormmm@taz",
|
||||||
|
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIGdTHkPCPUhvrcGgU9M6/BaEeirStM/kBnFxsLyXyelt syntaxerrormmm@kurotsuchi"
|
||||||
|
]
|
||||||
|
|
||||||
|
# Ansible configuration
|
||||||
|
playbooks = [
|
||||||
|
"deploy.yml"
|
||||||
|
]
|
||||||
|
|
||||||
|
os_to_template = {
|
||||||
|
"tpl-ci-debian12": 808,
|
||||||
|
"debian12": 808,
|
||||||
|
"bookworm": 808
|
||||||
|
}
|
||||||
|
|
@ -1,26 +0,0 @@
|
|||||||
---
|
|
||||||
pve:
|
|
||||||
url: "https://pve.vavassori.lcl:8006"
|
|
||||||
insecure: true
|
|
||||||
username: root@pam
|
|
||||||
nodename: pve
|
|
||||||
storage: local-lvm
|
|
||||||
|
|
||||||
# Basic VM configuration
|
|
||||||
vm:
|
|
||||||
name: virtual-machine
|
|
||||||
clone: 802
|
|
||||||
sockets: 1
|
|
||||||
cores: 1
|
|
||||||
ram: 2048
|
|
||||||
disksize: 20
|
|
||||||
username: syntaxerrormmm
|
|
||||||
password: cicciopasticcio
|
|
||||||
sshkeys:
|
|
||||||
- "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIFioHkaV1NhX6NCqsJakJw8EVBOcDHm1MEbpY499CPtG syntaxerrormmm@fisso"
|
|
||||||
- "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAILu91hBh8pNRt4eE1pug0Y4jCHZDCcMJ+vj3CiF5EQHV syntaxerrormmm@syntaxxps"
|
|
||||||
- "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAILH5q/ObtC4VhNT88gebezP/svpvCoQLoZCh4DvUn4xq syntaxerrormmm@taz"
|
|
||||||
- "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIGdTHkPCPUhvrcGgU9M6/BaEeirStM/kBnFxsLyXyelt syntaxerrormmm@kurotsuchi"
|
|
||||||
|
|
||||||
# Ansible configuration
|
|
||||||
ansible_playbook: deploy.yml
|
|
46
pvm-ansible/pvewrapper.py
Normal file
46
pvm-ansible/pvewrapper.py
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
# encoding: utf-8
|
||||||
|
|
||||||
|
from typing import List, Optional
|
||||||
|
from proxmoxer import ProxmoxAPI
|
||||||
|
import time
|
||||||
|
|
||||||
|
class PveWrapper:
|
||||||
|
|
||||||
|
def __init__(self, hostname: str, username: str, token_name: str, token_value: str, nodename: str, verify_ssl=False) -> None:
|
||||||
|
"""Wrapper for Proxmox API query."""
|
||||||
|
|
||||||
|
self.conn = ProxmoxAPI(
|
||||||
|
hostname,
|
||||||
|
user=username,
|
||||||
|
token_name=token_name,
|
||||||
|
token_value=token_value,
|
||||||
|
verify_ssl=verify_ssl)
|
||||||
|
|
||||||
|
self.instance = self.conn.nodes(nodename)
|
||||||
|
|
||||||
|
def ipv4_addresses(self, vmid: str) -> List[str]:
|
||||||
|
"""Returns the list of ipv4_addresses."""
|
||||||
|
|
||||||
|
netints = {}
|
||||||
|
while True:
|
||||||
|
try:
|
||||||
|
netints = self.instance.qemu(int(vmid)).agent.get("network-get-interfaces")
|
||||||
|
except:
|
||||||
|
time.sleep(10)
|
||||||
|
continue
|
||||||
|
else:
|
||||||
|
# When QEMU agent is running, return the array.
|
||||||
|
# All non-lo interfaces
|
||||||
|
filtered = [ x for x in netints['result'] if x['name'] != 'lo' ]
|
||||||
|
|
||||||
|
retval = []
|
||||||
|
for interface in filtered:
|
||||||
|
ipv4_addresses = [ x['ip-address'] for x in interface['ip-addresses'] if x['ip-address-type'] == 'ipv4' ]
|
||||||
|
# We assume a new machine has just been spawned, so it has just 1 ipv4 address.
|
||||||
|
ipv4_address = ipv4_addresses[0]
|
||||||
|
|
||||||
|
retval.append({ "name": interface['name'],
|
||||||
|
"ipv4_address": ipv4_address })
|
||||||
|
|
||||||
|
return retval
|
@ -2,3 +2,5 @@ pulumi>=3.0.0,<4.0.0
|
|||||||
pulumi-proxmoxve>=2.0.0
|
pulumi-proxmoxve>=2.0.0
|
||||||
pulumi-command>=0.4.1
|
pulumi-command>=0.4.1
|
||||||
dotmap>=1.3.30
|
dotmap>=1.3.30
|
||||||
|
requests>=2.32.0
|
||||||
|
proxmoxer>=2.0.0
|
||||||
|
Loading…
Reference in New Issue
Block a user