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:
|
||||
description: ProxmoxVE provider, Ansible powered with Python
|
||||
config:
|
||||
password:
|
||||
description: Password of the root user over PVE.
|
||||
pveHostname:
|
||||
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
|
||||
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_proxmoxve as proxmox
|
||||
import pulumi_command as command
|
||||
import yaml
|
||||
from dotmap import DotMap
|
||||
|
||||
# Import configuration parameters from input.yaml
|
||||
input_ = DotMap(yaml.safe_load(open("./input.yaml", "r").read()))
|
||||
from pvewrapper import PveWrapper
|
||||
import additional_config
|
||||
|
||||
# Check pulumi configuration
|
||||
config = pulumi.Config()
|
||||
|
||||
provider = proxmox.Provider("proxmoxve",
|
||||
endpoint=input_.pve.url,
|
||||
insecure=input_.pve.insecure,
|
||||
username=input_.pve.username,
|
||||
password=config.require_secret("password")
|
||||
username = config.require("pveTokenName").split("!")[0]
|
||||
tokenname = config.require("pveTokenName").split("!")[1]
|
||||
pvehostname = config.require("pveHostname")
|
||||
pvenodename = config.require("pveNodeName")
|
||||
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
|
||||
vm_username = input_.vm.username
|
||||
ansible_playbook = input_.ansible_playbook
|
||||
pve = PveWrapper(pvehostname,
|
||||
username=username,
|
||||
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,
|
||||
node_name = input_.pve.nodename,
|
||||
node_name = pvenodename,
|
||||
agent = proxmox.vm.VirtualMachineAgentArgs(
|
||||
enabled = True,
|
||||
trim = True,
|
||||
type = "virtio"
|
||||
),
|
||||
bios = "seabios",
|
||||
cpu = proxmox.vm.VirtualMachineCpuArgs(
|
||||
cores = input_.vm.cores,
|
||||
sockets = input_.vm.sockets
|
||||
cores = config.get_int("vmSockets"),
|
||||
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(
|
||||
node_name = input_.pve.nodename,
|
||||
vm_id = input_.vm.clone,
|
||||
full = True
|
||||
node_name = pvenodename,
|
||||
vm_id = additional_config.os_to_template[config.require("vmTemplate")],
|
||||
full = False
|
||||
),
|
||||
disks = [
|
||||
proxmox.vm.VirtualMachineDiskArgs(
|
||||
interface = "virtio0",
|
||||
datastore_id = input_.pve.storage,
|
||||
size = input_.vm.disksize,
|
||||
datastore_id = pvedefaultstorage,
|
||||
size = config.get_int("vmDiskSize"),
|
||||
file_format = "raw"
|
||||
)
|
||||
],
|
||||
@ -56,17 +65,16 @@ vm = proxmox.vm.VirtualMachine("vm",
|
||||
model = "virtio"
|
||||
)
|
||||
],
|
||||
on_boot = True,
|
||||
operating_system = proxmox.vm.VirtualMachineOperatingSystemArgs(
|
||||
type = "l26"
|
||||
),
|
||||
initialization = proxmox.vm.VirtualMachineInitializationArgs(
|
||||
type = "nocloud",
|
||||
datastore_id = input_.pve.storage,
|
||||
datastore_id = pvedefaultstorage,
|
||||
user_account = proxmox.vm.VirtualMachineInitializationUserAccountArgs(
|
||||
username = vm_username,
|
||||
password = input_.vm.password,
|
||||
keys = input_.vm.sshkeys
|
||||
password = config.require_secret("cloudPassword"),
|
||||
keys = additional_config.sshkeys
|
||||
)
|
||||
),
|
||||
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
|
||||
inventory = command.local.Command("a-inventory",
|
||||
create = vm.ipv4_addresses[1][0].apply(
|
||||
lambda ipaddr: f"echo '{vm_name} ansible_host={ipaddr} ansible_user={vm_username}' >./inventory"
|
||||
create = vm.ipv4_addresses.apply(lambda ipaddr:
|
||||
f"echo '{vm_name} ansible_host={ipaddr[0]['ipv4_address']} ansible_user={vm_username}' >./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
|
||||
execute_ansible = command.local.Command("a-deploy",
|
||||
create = vm.ipv4_addresses[1][0].apply(
|
||||
lambda ipaddr: f"ansible-playbook {ansible_playbook}"
|
||||
),
|
||||
delete = "rm -f ./ansible.log",
|
||||
opts = pulumi.ResourceOptions(depends_on = [ inventory ])
|
||||
)
|
||||
for playbook in additional_config.playbooks:
|
||||
shortname: str = playbook.split(".")[0]
|
||||
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",
|
||||
opts = pulumi.ResourceOptions(depends_on = [ inventory ])
|
||||
)
|
||||
|
||||
# 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-command>=0.4.1
|
||||
dotmap>=1.3.30
|
||||
requests>=2.32.0
|
||||
proxmoxer>=2.0.0
|
||||
|
Loading…
Reference in New Issue
Block a user