ca44934546
The interactive shell provides tab-completion for LXC-/VM-/Node-names and IDs. CLI provides storage-management, including listing storages and there contents. Also added: Listing and downloading of APLs to storages.
197 lines
7.6 KiB
Ruby
197 lines
7.6 KiB
Ruby
require 'ostruct'
|
|
|
|
module PVE::CTTemplate
|
|
class Base
|
|
attr_reader :options
|
|
|
|
def initialize **opts
|
|
@options = OpenStruct.new opts
|
|
@virts = Proxmox::LXC.all + Proxmox::Qemu.all
|
|
end
|
|
|
|
def name() options.name end
|
|
def node() options.node end
|
|
def arch() options.arch || 'amd64' end
|
|
def vmid() options.vmid end
|
|
def ostype() options.ostype end
|
|
def cmode() options.cmode || 'shell' end
|
|
def cores() options.cores || 1 end
|
|
def description() options.description || '' end
|
|
def hostname() options.hostname || name end
|
|
def memory() options.memory || 1024 end
|
|
def swap() options.swap || 0 end
|
|
def unprivileged() options.unprivileged() || 1 end
|
|
|
|
def ssh_public_keys
|
|
options[:'ssh-public-keys'] ||
|
|
File.read( options[:'ssh-public-keys-file'] || '/root/.ssh/authorized_keys')
|
|
end
|
|
|
|
def _ipv4 ip, gw
|
|
return [ip, nil] if %w[dhcp].include? ip
|
|
ip = IPAddress::IPv4.new ip
|
|
[ip.to_string, gw || ip.hosts.last.to_s]
|
|
end
|
|
|
|
def _ipv6 ip, gw
|
|
return [ip, nil] if %w[dhcp auto].include? ip
|
|
ip = IPAddress::IPv6.new ip
|
|
[ip.to_string, gw || ip.hosts.last.to_s]
|
|
end
|
|
|
|
def net0()
|
|
if options.ipv4 || options.ipv6
|
|
ipv4, gw4 = _ipv4( options.ipv4, options.gateway4)
|
|
ipv6, gw6 = _ipv6( options.ipv6, options.gateway6)
|
|
{
|
|
name: 'eth0',
|
|
bridge: 'vmbr1',
|
|
ip: ipv4, ip6: ipv6,
|
|
gw: gw4, gw6: gw6,
|
|
}
|
|
end
|
|
end
|
|
def net1() nil end
|
|
def net2() nil end
|
|
def net3() nil end
|
|
end
|
|
|
|
class Default < Base
|
|
def self.help
|
|
nil
|
|
end
|
|
|
|
def self.requirements
|
|
{
|
|
node: [:string, false, "Create CT on this node."],
|
|
name: [:string, true, "Set (uniq) name"],
|
|
arch: [:enum, false, "Architecture", %w[amd64 i386 arm64 armhf]],
|
|
vmid: [:numeric, true, "VM-ID. Proxmox internal number (100...)"],
|
|
ostype: [:string, true, "OS-Type (OS or distribution)"],
|
|
cmode: [:enum, false, "Console-mode", %w[shell console tty]],
|
|
cores: [:numeric, false, "Count of cores"],
|
|
description: [:string, false, "Description. Eg. What should this CT do?"],
|
|
hostname: [:string, false, "Hostname"],
|
|
memory: [:numeric, false, "How much memory CT could use?"],
|
|
swap: [:numeric, false, "How much CT can swap?"],
|
|
unprivileged: [:boolean, false, "Unprivileged are restricted to own UID/GID-space."],
|
|
:'ssh-public-keys' => [:string, false, "SSH-Public-Keys, which should be added to root-user in CT."],
|
|
:'ssh-public-keys-file' => [:string, false, "Read SSH-Public-Keys from file."],
|
|
ipv4: [:string, false, "IPv4-Address with net-size."],
|
|
gateway4: [:string, false, "IPv4-Address of gateway."],
|
|
ipv6: [:string, false, "IPv6-Address with net-size or auto."],
|
|
gateway6: [:string, false, "IPv6-Address of gateway."],
|
|
storage: [:string, false, "Device will be create on this Storage (default: local"],
|
|
}
|
|
end
|
|
end
|
|
|
|
class Datacenter < Base
|
|
def self.help
|
|
<<-EOF.gsub /^ {6}/, ''
|
|
Datacenter provides an interface for special network-settings.
|
|
Networks in Datacenters are often based on this behaviour:
|
|
A Network has an ID like 99.
|
|
This defines the VLANs: 2099 for Layer2/3099 for Layer3.
|
|
The IPv4-Range would be 10.99.0.0/16, but container will be put static in 10.99.255.0/24.
|
|
IPv6 uses RADV, so we do not need to know the IPv6-Range => auto.
|
|
VMID can be generated by Network-ID, too: smallest unused VMID in 100*ID.
|
|
EOF
|
|
end
|
|
|
|
def self.requirements
|
|
{
|
|
node: [:string, false, "Create CT on this node."],
|
|
name: [:string, true, "Set (uniq) name"],
|
|
arch: [:enum, false, "Architecture", %w[amd64 i386 arm64 armhf]],
|
|
vmid: [:numeric, false, "VM-ID. Proxmox internal number (100...)"],
|
|
ostype: [:string, true, "OS-Type (OS or distribution)"],
|
|
cmode: [:enum, false, "Console-mode", %w[shell console tty]],
|
|
cores: [:numeric, false, "Count of cores"],
|
|
description: [:string, false, "Description. Eg. What should this CT do?"],
|
|
hostname: [:string, false, "Hostname"],
|
|
memory: [:numeric, false, "How much memory CT could use?"],
|
|
swap: [:numeric, false, "How much CT can swap?"],
|
|
unprivileged: [:boolean, false, "Unprivileged are restricted to own UID/GID-space."],
|
|
:'ssh-public-keys' => [:string, false, "SSH-Public-Keys, which should be added to root-user in CT."],
|
|
:'ssh-public-keys-file' => [:string, false, "Read SSH-Public-Keys from file."],
|
|
:'network-id' => [:numeric, true, "Put Container to this VLAN and use a random IPv4-Address for this CT."],
|
|
ipv4: [:string, false, "IPv4-Address with net-size or dhcp."],
|
|
gateway4: [:string, false, "IPv4-Address of gateway."],
|
|
ipv6: [:string, false, "IPv6-Address with net-size or auto|dhcp."],
|
|
gateway6: [:string, false, "IPv6-Address of gateway."],
|
|
storage: [:string, false, "Device will be create on this Storage (default: root)"],
|
|
ostemplate: [:string, false, "OS-Template eg. local:vztmpl/superlinux-1.2-amd64.tar.xz"],
|
|
}
|
|
end
|
|
|
|
def node() options.node || 'svc1' end
|
|
def ostype() options.ostype || 'debian' end
|
|
def memory() options.memory || 2048 end
|
|
def storage() options.storage || 'root' end
|
|
|
|
def network_id
|
|
return @network_id if @network_id
|
|
expect = 0..12
|
|
nid = options[:'network-id']
|
|
unless nid == nid.to_i.to_s && expect.include?( nid.to_i)
|
|
raise ArgumentError, "Network ID must be #{expect.inspect}. Given: #{nid.inspect}"
|
|
end
|
|
@network_id = nid.to_i
|
|
end
|
|
|
|
def net0
|
|
ipv4, gw4 =
|
|
if options.ipv4
|
|
_ipv4( options.ipv4, options.gateway4)
|
|
else
|
|
self.ipv4_gw
|
|
end
|
|
ipv6, gw6 =
|
|
if options.ipv6
|
|
_ipv6( options.ipv6, options.gateway6)
|
|
else
|
|
['auto', nil]
|
|
end
|
|
{
|
|
name: 'eth0',
|
|
bridge: 'vmbr1',
|
|
tag: 2000+network_id,
|
|
mtu: 9166,
|
|
firewall: 1,
|
|
ip: ipv4, gw: gw4,
|
|
ip6: ipv6, gw6: gw6,
|
|
}.delete_if {|k,v| v.nil? }
|
|
end
|
|
|
|
def vmid
|
|
super || ((0...100).map {|i| "#{100*network_id+i}" } - @virts.map( &:vmid)).first
|
|
end
|
|
|
|
def network
|
|
IPAddress::IPv4.new "10.#{network_id}.255.0/24"
|
|
end
|
|
|
|
def ipv4_gw
|
|
return @ipv4_gw if @ipv4_gw
|
|
ipv4s = network.hosts
|
|
@virts.each do |v|
|
|
v.config[:network].each {|n| ipv4s.delete n[:ip] if n[:ip] }
|
|
end
|
|
@ipv4_gw = [ipv4s.first.to_string, network.last.to_s]
|
|
end
|
|
|
|
def ostemplate
|
|
@ostemplate ||=
|
|
options.ostemplate ||
|
|
case ostype
|
|
when 'debian'
|
|
# TODO: how to determine which template?
|
|
'local:vztmpl/debian-11-standard_11.0-1_amd64.tar.gz'
|
|
else
|
|
raise ArgumentError, "OS-Template for ostype #{ostype} not found or ostemplate not provided."
|
|
end
|
|
end
|
|
end
|
|
end
|