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:
Emiliano Vavassori 2024-09-09 02:09:20 +02:00
parent 26e4cb28f2
commit 529dfe9be2
6 changed files with 174 additions and 70 deletions

View File

@ -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.

View File

@ -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(
delete = "rm -f ./ansible.log", lambda run: f"ansible-playbook {run[0]['ipv4_address']}"
opts = pulumi.ResourceOptions(depends_on = [ inventory ]) ),
) delete = "rm -f ./ansible.log",
opts = pulumi.ResourceOptions(depends_on = [ inventory ])
)
# Outputs # Outputs
pulumi.export("ip", vm.ipv4_addresses[1][0]) pulumi.export("ipv4_addresses", vm.ipv4_addresses)

View 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
}

View File

@ -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
View 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

View File

@ -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