From def708e27155f7ff039c9f074d3c4d9af813da3d Mon Sep 17 00:00:00 2001 From: root Date: Wed, 7 Sep 2022 02:35:53 +0200 Subject: [PATCH] siteconf --- .gitignore | 1 + etalon | 213 +++++++++++++++++++++++++++++++++++++++++++++++++++ etalon_clone | 204 ++++++++++++++++++++++++++++++++++++++++++++++++ gather_facts | 13 ++-- siteconf.pm | 32 ++++++++ siteconf.py | 29 +++++++ 6 files changed, 487 insertions(+), 5 deletions(-) create mode 100644 .gitignore create mode 100755 etalon create mode 100755 etalon_clone create mode 100755 siteconf.pm create mode 100755 siteconf.py diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..bee8a64 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +__pycache__ diff --git a/etalon b/etalon new file mode 100755 index 0000000..90cc39d --- /dev/null +++ b/etalon @@ -0,0 +1,213 @@ +#!/usr/bin/perl +use strict; use warnings; use utf8; +use FindBin; +use lib $FindBin::Bin; +binmode STDOUT, ':utf8'; +binmode STDERR, ':utf8'; +use Getopt::Long qw(:config no_ignore_case bundling); +use siteconf; + +my %site_conf = siteconf::read; + +my %opts = ( + tmpmask => 24, + tmpip_skip => 0, + nodeploy => 0, + netmask => 24, + nouzem => 0, + noweb => 0, + nomysql => 0, + noreboot => 0 + ); +my $usage = < \$opts{site}, + "clone-ip=s" => \$opts{clone_ip}, + "tmpip=s" => \$opts{tmpip}, + "tmpmask=s" => \$opts{tmpmask}, + "tmpgw=s" => \$opts{tmpgw}, + "tmpip-skip" => \$opts{tmpip_skip}, + "nodeploy" => \$opts{nodeploy}, + "hostname=s" => \$opts{hostname}, + "domain=s" => \$opts{domain}, + "dns=s" => \$opts{dns}, + "ntp=s" => \$opts{ntp}, + "ip=s" => \$opts{ip}, + "netmask=s" => \$opts{netmask}, + "gw=s" => \$opts{gw}, + "datasize=s" => \$opts{datasize}, + "nouzem" => \$opts{nouzem}, + "noweb" => \$opts{noweb}, + "nomysql" => \$opts{nomysql}, + "mysql-root=s" => \$opts{mysql_root}, + "border=s" => \$opts{border}, + "border-db=s" => \$opts{border_db}, + "border-web=s" => \$opts{border_web}, + "border-web-dbhost=s" => \$opts{border_web_dbhost}, + "border-web-dbip=s" => \$opts{border_web_dbip}, + "border-dbpasswd=s" => \$opts{border_dbpasswd}, + "noreboot" => \$opts{noreboot}, + "help|h" => sub {print $usage; exit 0} + ) or die $usage; + +if (!defined $opts{site}) { + die "Kell --site.\n"; +} +if (!exists $site_conf{$opts{site}}) { + die "site not found: $opts{site}\n"; +} +for my $o (qw(dns ntp domain clone_ip mgmt_ssh_key postfix_relayhost)) { + if (!defined $opts{$o}) { + $opts{$o} = $site_conf{$opts{site}}->{$o}; + } +} + +chdir("$FindBin::Bin/..") or die "chdir() failed.\n"; + +my @vars; +my @skip; + +if ($site_conf{$opts{site}}->{aptproxy}) { + push @vars, "aptproxy=$site_conf{$opts{site}}->{aptproxy}"; +} + +my $border_opts = 0; +for my $x ($opts{border}, $opts{border_web}, $opts{border_db}) { + $border_opts++ if $x; +} +die "Csak egy --border... opciót lehet egyszerre megadni\n" if $border_opts > 1; + +if ($opts{border}) { + if ($opts{noweb} or $opts{nomysql}) { + die "--border opcióhoz kell web és mysql.\n"; + } + push @vars, "border_prefix=$opts{border}"; +} elsif ($opts{border_web}) { + die "Kell web.\n" if $opts{noweb}; + die "Kell --mysql-root.\n" if !defined $opts{mysql_root}; + die "Kell dbhost.\n" if !defined $opts{border_web_dbhost}; + die "Kell dbip.\n" if !defined $opts{border_web_dbip}; + die "Kell dbpasswd.\n" if !defined $opts{border_dbpasswd}; + push @vars, "border_prefix=$opts{border_web}"; + push @vars, "border_install_webonly=1"; + push @vars, "border_dbhost=$opts{border_web_dbhost}"; + push @vars, "border_dbip=$opts{border_web_dbip}"; +} elsif ($opts{border_db}) { + die "Kell mysql.\n" if $opts{nomysql}; + die "Kell --mysql-root.\n" if !defined $opts{mysql_root}; + die "Kell dbpasswd.\n" if !defined $opts{border_dbpasswd}; + push @vars, "border_prefix=$opts{border_db}"; + push @vars, "border_install_dbonly=1"; +} else { + push @skip, 'border'; +} + +if (!$opts{nodeploy}) { + die "Missing --hostname.\n" if !defined $opts{hostname}; + die "Missing --ip.\n" if !defined $opts{ip}; + if (!defined $opts{gw}) { + $opts{gw} = $opts{ip}; + $opts{gw} =~ s/\.\d+$/.254/; + } + for my $x (qw(hostname domain dns ntp ip netmask gw)) { + push @vars, "$x=$opts{$x}"; + } +} +if ($opts{datasize}) { + push @vars, "datasize=$opts{datasize}"; +} +if ($opts{mysql_root}) { + push @vars, "mysql_root=$opts{mysql_root}"; +} +if ($opts{border_dbpasswd}) { + push @vars, "border_dbpasswd=$opts{border_dbpasswd}"; +} +if ($opts{postfix_relayhost}) { + push @vars, "postfix_relayhost=$opts{postfix_relayhost}"; +} + +if ($opts{tmpip}) { + if (!$opts{tmpip_skip}) { + if (!defined $opts{tmpgw}) { + $opts{tmpgw} = $opts{tmpip}; + $opts{tmpgw} =~ s/\.\d+$/.254/; + } + my $cmd = "ansible -i etalon-debian, -e ansible_host=$opts{clone_ip} ". + "-u root -B 30 -P 0 -m shell -a '". + "sleep 3; DEV=\$(cd /sys/class/net; \\ls -1d e* | head -1);". + "ip a flu dev \$DEV; ". + "ip a add $opts{tmpip}/$opts{tmpmask} dev \$DEV; ". + "ip r add default via $opts{tmpgw}; ". + "ping -c 3 $opts{tmpgw}". + "' etalon-debian"; + print "$cmd\n"; + system $cmd; + exit 1 if $? > 0; + print "New IP address: $opts{tmpip}.\n"; + sleep 5; + } + push @vars, "ansible_host=$opts{tmpip}"; +} + +my $vars = ''; +for my $v (@vars) { + if ($v =~ /[\s'"]/) { + die "invalid character: '$v'.\n"; + } + $vars .= " -e '$v'"; +} + +if ($opts{mgmt_ssh_key}) { + # szokoz van benne: -e 'aaa="bb cc"' + $vars .= " -e 'mgmt_ssh_key=\"$opts{mgmt_ssh_key}\"'"; +} + + +for my $x (qw(deploy uzem web mysql reboot)) { + push @skip, $x if $opts{"no$x"}; +} +my $skip = ''; +if (scalar @skip) { + $skip = "--skip-tags=". join(',', @skip); +} +my $cmd = "ansible-playbook -i etalon-debian, -e ansible_host=$opts{clone_ip} $skip $vars etalon.yml"; + +print "$cmd\n"; +system $cmd; +exit 1 if $? > 0; + +if (!$opts{nodeploy}) { + print "\nadd to inventory:\n\n". + "echo '$opts{hostname} $opts{ip}' >> inventory\n\n"; +} + +# vim: set tabstop=4 shiftwidth=4 expandtab smarttab: diff --git a/etalon_clone b/etalon_clone new file mode 100755 index 0000000..5dc44a0 --- /dev/null +++ b/etalon_clone @@ -0,0 +1,204 @@ +#!/usr/bin/env python3 +from pyVmomi import vim +import pyVim.connect +import atexit +import argparse +from pprint import pprint +import siteconf + +site_conf = siteconf.read() + +def get_args(): + parser = argparse.ArgumentParser( + description='Clone VM from template') + + parser.add_argument('-s', '--site', + required=True, + action='store', + help='Name of the site to create VM on') + + parser.add_argument('-t', '--template', + required=False, + action='store', + help='Name of the template you wish to clone') + + parser.add_argument('-v', '--vm-name', + required=True, + action='store', + help='Name of the VM you wish to make') + + parser.add_argument('-c', '--cpu', + required=False, + type=int, + action='store', + default=1, + help='Set number of CPUs') + + parser.add_argument('-m', '--memory', + required=False, + type=int, + action='store', + default=2048, + help='[MB] Set memory size') + + parser.add_argument('-d', '--disk-size', + required=False, + type=int, + action='store', + default=0, + help='[GB] Resize LVM disk to this size') + + parser.add_argument('--datastore', + required=False, + action='store', + default=None, + help='target datastore') + + args = parser.parse_args() + args.disk_size = args.disk_size * 1024 * 1024 + return args + + +def wait_for_task(task): + """ wait for a vCenter task to finish """ + task_done = False + while not task_done: + if task.info.state == 'success': + return task.info.result + + if task.info.state == 'error': + print("there was an error") + task_done = True + + +def get_obj(content, vimtype, name): + """ + Return an object by name, if name is None the + first found object is returned + """ + obj = None + container = content.viewManager.CreateContainerView( + content.rootFolder, vimtype, True) + for c in container.view: + if name: + if c.name == name: + obj = c + break + else: + obj = c + break + + return obj + + +def clone_vm(content, template, vm_name, datacenter_name, datastore_name, + cluster_or_host): + datacenter = get_obj(content, [vim.Datacenter], datacenter_name) + destfolder = datacenter.vmFolder + datastore = get_obj(content, [vim.Datastore], datastore_name) + cluster = get_obj(content, [vim.ClusterComputeResource], cluster_or_host) + if cluster: + resource_pool = cluster.resourcePool + else: + host = get_obj(content, [vim.ComputeResource], cluster_or_host) + resource_pool = host.resourcePool + + relospec = vim.vm.RelocateSpec() + relospec.datastore = datastore + relospec.pool = resource_pool + clonespec = vim.vm.CloneSpec() + clonespec.location = relospec + clonespec.powerOn = False + + print("cloning VM...") + task = template.Clone(folder=destfolder, name=vm_name, spec=clonespec) + wait_for_task(task) + +def extend_disk(content, vm_name, extend_to_kb): + vm = get_obj(content, [vim.VirtualMachine], vm_name) + for dev in vm.config.hardware.device: + #print("dev "+ dev.deviceInfo.label) + if hasattr(dev.backing, 'fileName'): + size_kb = dev.capacityInKB + if size_kb > 5 * 1024 * 1024: # lvm disk + if extend_to_kb > size_kb: + disk_spec = vim.vm.device.VirtualDeviceSpec() + disk_spec.operation = vim.vm.device.VirtualDeviceSpec.Operation.edit + disk_spec.device = vim.vm.device.VirtualDisk() + disk_spec.device.key = dev.key + disk_spec.device.backing = vim.vm.device.VirtualDisk.FlatVer2BackingInfo() + disk_spec.device.backing.fileName = dev.backing.fileName + disk_spec.device.backing.diskMode = dev.backing.diskMode + disk_spec.device.controllerKey = dev.controllerKey + disk_spec.device.unitNumber = dev.unitNumber + disk_spec.device.capacityInKB = extend_to_kb + dev_changes = [] + dev_changes.append(disk_spec) + spec = vim.vm.ConfigSpec() + spec.deviceChange = dev_changes + print("extending disk '"+ dev.deviceInfo.label +"' to "+ str(extend_to_kb) +" kB") + task = vm.ReconfigVM_Task(spec=spec) + wait_for_task(task) + +def set_cpu_mem(content, vm_name, cpu, mem_mb): + vm = get_obj(content, [vim.VirtualMachine], vm_name) + spec = vim.vm.ConfigSpec() + spec.numCPUs = cpu + spec.memoryMB = mem_mb + print("Set cpu="+ str(cpu) +", memory="+ str(mem_mb) +" MB") + task = vm.ReconfigVM_Task(spec=spec) + wait_for_task(task) + +def power_on(content, vm_name): + vm = get_obj(content, [vim.VirtualMachine], vm_name) + print("Powering on '"+ vm_name +"'") + task = vm.PowerOn() + wait_for_task(task) + +def main(): + args = get_args() + + if not args.site in site_conf: + print(f"site not found: {args.site}") + exit(1) + site = site_conf[args.site]; + #pprint(site) + + si = pyVim.connect.SmartConnect( + host = site["vsphere"], + user = site["user"], + pwd = site["passwd"], + disableSslCertValidation = True + ) + + atexit.register(pyVim.connect.Disconnect, si) + + content = si.RetrieveContent() + template_name = site["template"] + if args.template is not None: + template_name = args.template + template = get_obj(content, [vim.VirtualMachine], template_name) + if not template: + print("template not found") + exit(1) + + vm = get_obj(content, [vim.VirtualMachine], args.vm_name) + if vm: + print("VM '"+ args.vm_name +"' exists.") + exit(1) + + datastore = site["datastore"] + if args.datastore is not None: + datastore = args.datastore + clone_vm(content, template, args.vm_name, site["datacenter"], + datastore, site["cluster_or_host"]) + + set_cpu_mem(content, args.vm_name, args.cpu, args.memory) + extend_disk(content, args.vm_name, args.disk_size) + power_on(content, args.vm_name) + + +if __name__ == "__main__": + main() + +# vim: set tabstop=4 shiftwidth=4 expandtab smarttab: diff --git a/gather_facts b/gather_facts index a0dacf2..e3aa3b3 100755 --- a/gather_facts +++ b/gather_facts @@ -1,16 +1,19 @@ #!/bin/bash ANSIBLE_DIR=/etc/ansible +ANSIBLE_CMDB=/usr/local/bin/ansible-cmdb OUTPUT=/var/www/def/public/facts.html rm $ANSIBLE_DIR/gathered_facts/* ansible '!off,linux,windows' -m setup -f 5 --task-timeout 30 --tree $ANSIBLE_DIR/gathered_facts >/dev/null -INV="" -if [[ -r $ANSIBLE_DIR/cmdb-inventory ]]; then - INV="-i $ANSIBLE_DIR/cmdb-inventory" +if [[ -x $ANSIBLE_CMDB ]]; then + INV="" + if [[ -r $ANSIBLE_DIR/cmdb-inventory ]]; then + INV="-i $ANSIBLE_DIR/cmdb-inventory" + fi + + $ANSIBLE_CMDB $INV -t html_fancy_split_overview $ANSIBLE_DIR/gathered_facts > $OUTPUT fi -/usr/local/bin/ansible-cmdb $INV -t html_fancy_split_overview $ANSIBLE_DIR/gathered_facts > $OUTPUT - # vim: set tabstop=4 shiftwidth=4 expandtab smarttab: diff --git a/siteconf.pm b/siteconf.pm new file mode 100755 index 0000000..d814ffe --- /dev/null +++ b/siteconf.pm @@ -0,0 +1,32 @@ +package siteconf; +use strict; use warnings; +use FindBin; + +my $configuration = "$FindBin::Bin/../site.conf"; +if (!open(FH, '<', $configuration)) { + die "Could not open configuration file: $configuration\n"; +} + +my %site_conf; +sub read { + my $site; + while (my $line = ) { + chomp $line; + next if $line =~ /^#/; + next if $line !~ /\S/; + if ($line =~ /^\[([A-Za-z0-9_-]+)\]/) { + $site = $1; + $site_conf{$site} = {}; + } elsif ($line =~ /^([A-Za-z0-9_-]+)\s+(.+)/) { + $site_conf{$site}->{$1} = $2; + } else { + die "invalid line in site_conf: $line\n"; + } + } + close(FH); + return %site_conf; +} + +1; +__END__ +# vim: set tabstop=4 shiftwidth=4 expandtab smarttab: diff --git a/siteconf.py b/siteconf.py new file mode 100755 index 0000000..5803d50 --- /dev/null +++ b/siteconf.py @@ -0,0 +1,29 @@ +import os +import re +#from pprint import pprint + +configuration = "{}/../site.conf".format(os.path.dirname(__file__)) +site_conf = {} + +def read(): + with open(configuration, "r") as cf: + for line in cf: + line = line.rstrip(); + if r := re.match('#', line): + continue + if r := re.match('\s*$', line): + continue + + if r := re.match('\[([A-Za-z0-9_-]+)\]', line): + site = r[1] + site_conf[site] = {} + elif r := re.match('([A-Za-z0-9_-]+)\s+(.+)', line): + site_conf[site][r[1]] = r[2] + else: + print(f"invalid line in site_conf: {line}") + exit(1) + + cf.close() + return site_conf + +# vim: set tabstop=4 shiftwidth=4 expandtab smarttab: