lxctl-0.3.1+debian/0000755000175000017500000000000011652615116014323 5ustar sysadminsysadminlxctl-0.3.1+debian/README.md0000644000175000017500000000132611652614527015611 0ustar sysadminsysadminlxctl ===== perl program to control lxc-based containers in vzctl-like way. Contributing ------------ http://bugs.lxc.tl/ Installation ----------- make DESTDIR="/usr/local/" install Usage ----- $ lxctl --help or $ lxctl --man Contributing ------------ Feel free to contact us via email: Anatoly Burtsev, anatolyburtsev@yandex.ru Pavel Potapenkov, ppotapenkov@gmail.com Vladimir Smirnov, civil.over@gmail.com Copyrighting and License ------------------------ Copyright (C) 2011 by Anatoly Burtsev, Pavel Potapenkov, Vladimir Smirnov This script is free software; you can redistribute it and/or modify it under the same terms of GPL v2 or later, or, at your opinion under terms of artistic license. lxctl-0.3.1+debian/lxctl0000755000175000017500000001416611652614535015413 0ustar sysadminsysadmin#!/usr/bin/perl -w use strict; use warnings; # Comment out next option before release! #use diagnostics -verbose; use Getopt::Long; use Pod::Usage; use Digest::SHA qw(sha1_hex); use Exporter 'import'; use Lxc::object; use LxctlHelpers::helper; use LxctlHelpers::config; our $version = "0.3.0"; our $api_ver = 1; our $Lxc = Lxc::object->new; sub help_me { my $action = shift; return if !$action; if ($action eq '--version' || $action eq '-v') { print "Version: $version\n"; exit 0; } if ($action eq '--help' || $action eq '-h') { pod2usage(1); } $Lxc->check(); } sub lets_rock { my $config = new LxctlHelpers::config; $config->load_main(); my $action = shift @ARGV; help_me($action); my $class; my $helper = new LxctlHelpers::helper; eval { $helper->load_module("Lxctl/$action.pm"); $action = "Lxctl::$action"; $action->import; $class = new $action; } or do { #die "Unsupported command!\n\n$@\n\n"; die "$@"; }; $class->do(@ARGV); return; } lets_rock; __END__ =head1 NAME lxctl - Control various aspects of lxc. =head1 SYNOPSIS lxctl [action] [vmname] [options] See lxctl --man or lxctl --help for more info =head1 OPTIONS =over 8 =item B<--help> Print a breif help message and exists =item B<--man> Prints the manual page and exits. =item B Starts container specified in 1st argument B vmname - name of the container =item B Stops container specified in 1st argument B vmname - name of the container =item B Creates container. B vmname - name of the container B --ipaddr - IP address of the machine --mask/netmask - network mask of the machine --defgw - default gateway of the machine --dns - primary DNS server --ostemplate - template name, by default it is 'lucid_amd64' --config - path to configuration file, by default /etc/lxc/ is used --root - path to root file system, by default /var/lxc/ is used --addpkg - list of additional packages (comma-separated) --pkgopt - list of additional packet manager options (space-separated, but as one argument) --rootsz - size of logical volume for root FS, by default it is 10G --hostname - sets the hostname of the machine, by default is used --searchdomain - set a custom searchdomain in /etc/resolv.conf --macaddr - set the custom mac adress of the container --autostart - autostart container each reboot host machine --no-save - do not save yaml config for new container, by default $CONF_PATH/vmname.yaml is used --load - create container from yaml config --debug - show more information about install process --tz - set custom timezone (Europe/Moscow, UTC, etc) --empty - create a clear container for migrate here =item B Changes container parameters. B vmname - name of the container B --rootsz - increment of size of logical volume for root FS --ipaddr - IP address if the machine --mask/netmask - network mask of the machine --defgw - default gateway of the machine --dns - primary DNS server --hostname - sets the hostname of the machine --searchdomain - set a custom searchdomain in /etc/resolv.conf --macaddr - set the custom mac adress if the machine --userpasswd user:passwd - sets password for given user --onboot {yes,no} - makes containet [do not] start at boot --tz - set custom timezone (Europe/Moscow, UTC, etc) --cpu-shares - sets the CPU share of the container --cpus - sets the CPU cores of the container --mem - sets the memory share of the container (in bytes!) --io - sets the IO share of the container =item B Freezes container B vmname - name of the container =item B Unfreezes container B vmname - name of the container =item B Lists all containers B --ipaddr - display with IP addr --hostname - display with hostname. --cgroup - display with cgroup --mount - display with mount point for rootfs --diskspace - display with free/full size --all - display all information --raw - display only vmnames =item B Migrate container from localhost to remote host. B --vmname - container name --tohost - to which host we should migrate B --remuser - remote username for ssh --remport - remote port for ssh --remname - remote container name --onboot - start on boot? 1 or 0 --userpasswd - 'user:password' formatted password for user --clone - cloning, a little bit faster and softer then simple migration --rootsz - remote root fs size --afterstart - start local container again after migration --cpus - cpus allocated to container --cpu-shares - cpu time share of the container --mem - memory limit of the container --io - IO throughput --ipaddr - IP of the remote container --searchdomain - DNS search domain of the container --netmask - network mask --defgw - default gateway --dns - DNS server =item B Migrate VZ-container from remote host to local LXC container. B --vmname - container name --fromhost - from which host we should migrate --remname - remote container name B --remuser - remote username for ssh --remport - remote port for ssh --onboot - start on boot? 1 or 0 --rootsz - remote root fs size --afterstart - start local container again after migration --cpus - cpus allocated to container --cpu-shares - cpu time share of the container --mem - memory limit of the container --io - IO throughput =back =head1 DESCRIPTION B controls lxc :) Man page by Capitan Obvious. =head1 AUTHOR Anatoly Burtsev, Eanatolyburtsev@yandex.ruE Pavel Potapenkov, Eppotapenkov@gmail.comE Vladimir Smirnov, Ecivil.over@gmail.comE =head1 COPYRIGHT AND LICENSE Copyright (C) 2011 by Anatoly Burtsev, Pavel Potapenkov, Vladimir Smirnov This script is free software; you can redistribute it and/or modify it under the same terms of GPL v2 or later, or, at your opinion under terms of artistic license. =cut lxctl-0.3.1+debian/TODO0000644000175000017500000000105111652614527015015 0ustar sysadminsysadminLxc::control: * Write makefile * Make normal tests * Determine if and how we can make test to create container. * Die on all errors, lxctl should use exceptions to handle this situation. * Stop phisically deleted but running container. * GHOST state detection. lxctl: * GHOST state detection. * GHOST state fix ability. * Check if container is running before destroying. * Workaround LVB bug with LV names (eg. if LV test exists, creating of test1 will fail) - bug is not working! * Check IP correctness. * IP-based MAC-address generaton. lxctl-0.3.1+debian/yaml-test.pl0000755000175000017500000000174711652614527016620 0ustar sysadminsysadmin#!/usr/bin/perl use strict; use warnings; use Lxc::object; use YAML::Tiny; my $yaml = YAML::Tiny->new; $yaml->[0]->{paths} = {LXC_CONF_DIR => '/var/lib/lxc', ROOTS_PATH => '/var/lxc/root', CONFIG_PATH => '/var/lib/lxc', TEMPLATE_PATH => '/var/lxc/templates', }; $yaml->[0]->{lvm} = {VG => 'vg0', }; $yaml->write('config.yaml'); $yaml = YAML::Tiny->read('config2.yaml'); my $lxc = new Lxc::object; $lxc->set_lxc_conf_dir($yaml->[0]->{paths}->{LXC_CONF_DIR}); $lxc->set_roots_path($yaml->[0]->{paths}->{ROOTS_PATH}); $lxc->set_config_path($yaml->[0]->{paths}->{CONFIG_PATH}); $lxc->set_template_path($yaml->[0]->{paths}->{TEMPLATE_PATH}); $lxc->set_vg($yaml->[0]->{lvm}->{VG}); my $tmp = $lxc->get_lxc_conf_dir(); print "LXC_CONF_DIR is $tmp\n"; $tmp = $lxc->get_roots_path(); print "LXC_CONF_DIR is $tmp\n"; $tmp = $lxc->get_config_path(); print "CONFIG_PATH is $tmp\n"; $tmp = $lxc->get_template_path(); print "TEMPLATE_PATH is $tmp\n"; $tmp = $lxc->get_vg(); print "VG is $tmp\n"; lxctl-0.3.1+debian/test_oop.pl0000755000175000017500000000201311652614527016520 0ustar sysadminsysadmin#!/usr/bin/perl use Lxc::object; use Test::More tests => 6; my $lxc = new Lxc::object; my $tmp = $lxc->check(); my $tmp2 = $lxc->get_vg(); $lxc->set_vg("TEMP!"); $tmp = $lxc->get_vg(); $lxc->set_vg($tmp2); eval { $tmp = $lxc->get_conf("testvm", "lxc.rootfs"); } or do { print "Can't run, testvm is not avaliable\n"; }; ok(defined($lxc->get_config_path()), "Should be defined"); ok(defined($lxc->get_roots_path()), "Should be defined"); ok(defined($lxc->get_template_path()), "Should be defined"); ok(defined($lxc->get_lxc_conf_dir()), "Should be defined"); ok(defined($lxc->get_cgroup_path()), "Should be defined"); ok(defined($lxc->get_vg()), "Should be defined"); $tmp = $lxc->get_config_path(); print "CONFIG_PATH: $tmp\n"; $tmp = $lxc->get_roots_path(); print "ROOTS_PATH: $tmp\n"; $tmp = $lxc->get_template_path(); print "TEMPLATE_PATH: $tmp\n"; $tmp = $lxc->get_lxc_conf_dir(); print "LXC_CONF_DIR: $tmp\n"; $tmp = $lxc->get_cgroup_path(); print "CGROUP_PATH: $tmp\n"; $tmp = $lxc->get_vg(); print "VG: $tmp\n"; lxctl-0.3.1+debian/lxctl.yaml0000644000175000017500000000112611652614527016342 0ustar sysadminsysadmin--- lvm: VG: vg00 paths: YAML_CONFIG_PATH: '/etc/lxctl' LXC_CONF_DIR: '/var/lib/lxc' ROOT_MOUNT_PATH: '/var/lxc/root' TEMPLATE_PATH: '/var/lxc/templates' LXC_LOG_PATH: '/var/log/lxc.log' LXC_LOG_LEVEL: 'DEBUG' check: skip_kernel_config_check: 1 rsync: RSYNC_OPTS: -aH --delete --numeric-ids --exclude 'proc/*' --exclude 'sys/*' -e ssh root: ROOT_SIZE: 50G ROOT_TYPE: lvm fs: FS: ext4 FS_OPTS: -b 4096 FS_MOUNT_OPTS: defaults,barrier=0 os: OS_TEMPLATE: ubuntu-10.04-amd64 set: SEARCHDOMAIN: ru IFNAME: 'ip' list: COLUMNS: 'name,disk_free_mb,status,ip,hostname' lxctl-0.3.1+debian/LxctlHelpers/0000755000175000017500000000000011652614527016741 5ustar sysadminsysadminlxctl-0.3.1+debian/LxctlHelpers/getters.pm0000644000175000017500000000241411652614527020755 0ustar sysadminsysadminpackage LxctlHelpers::getters; use strict; use warnings; use Lxc::object; sub get_ip{ my ($self, $name) = @_; my $subname = (caller(0))[3]; if (!defined($name)) { die "$subname: No vmname is given\n"; } my $path = $self->{'lxc'}->get_conf($name, "lxc.rootfs"); $path = $path . "/etc/network/interfaces"; open my $config_file, '<', "$path" or return "0.0.0.0"; my @interfaces = <$config_file>; my @ip = grep { /address / } @interfaces; return "0.0.0.0" if (scalar(@ip) == 0); $ip[0] =~ s/ address //; close($config_file); return "0.0.0.0" if (!defined($ip[0])); chop($ip[0]); return "$ip[0]"; } sub new { my $class = shift; my $self = {}; bless $self, $class; $self->{'lxc'} = new Lxc::object; return $self; } 1; __END__ =head1 AUTHOR Anatoly Burtsev, Eanatolyburtsev@yandex.ruE Pavel Potapenkov, Eppotapenkov@gmail.comE Vladimir Smirnov, Ecivil.over@gmail.comE =head1 COPYRIGHT AND LICENSE Copyright (C) 2011 by Anatoly Burtsev, Pavel Potapenkov, Vladimir Smirnov This library is free software; you can redistribute it and/or modify it under the same terms of GPL v2 or later, or, at your opinion under terms of artistic license. =cut lxctl-0.3.1+debian/LxctlHelpers/helper.pm0000644000175000017500000001142611652614527020562 0ustar sysadminsysadminpackage LxctlHelpers::helper; use strict; use warnings; use autodie qw(:all); sub fool_proof { use Term::ANSIColor; if (! -t STDIN || ! -t STDOUT) { print STDERR color 'bold red'; print STDERR "Warning! Non-interactive terminal. You have 5 seconds to cancel\n"; print STDERR color 'reset'; sleep 5; return 1; } my ($self) = @_; my $expected_answer = "Yes, all harm from this operation will be a result of my stupidity"; my $answer = ""; print color 'bold red'; print "WARNING! "; print color 'reset'; print "You are about to do something really terrible. This action may cause massive data loss.\n"; print "If you are sure, please enter: \"$expected_answer\" (without quotes, case sensitive or specify -f(--force) option)\n"; chomp($answer = ); if ("$answer" eq "$expected_answer") { print "Have a lot of fun...\n"; } else { die "Aborted!"; } } # Trys to require module, if success, returns 1, otherwise dies. sub load_module { my ($self, $module) = @_; my $dest = ""; foreach my $path (@INC) { if (-e "$path/$module") { $dest = "$path/$module"; last; } }; if (!$dest) { die "Unsupported command!\nUsage:\nlxctl [action] [vmname] [options]\n\nSee lxctl --man or lxctl --help for more info\n"; } eval { require $dest; } or do { die "$@\n\n"; }; return 1; } # Delete from config file. sub delete_config #(filename, option_name) { my ($self, $filename, $what) = @_; open(my $file, '<', "$filename"); my @content = <$file>; my $status = 0; close $file; @content = grep(!/^$what/, @content); open($file, '>', "$filename"); print $file @content; close $file; return $status; } # Destructive change for config file. # Changes OPT="what ever" to OPT=newval sub change_config #(filename, searchstring, newvalue) { my ($self, $filename, $what, $newval) = @_; if ( ! -e "$filename" ) { # If it's invalid symlink, open will fail, but "! -e" will return true # We need to unlink file before continue eval { unlink "$filename"; }; open(my $file, ">", "$filename"); print $file "$what $newval\n"; close($file); return 0; } open(my $file, '<', "$filename"); my @content = <$file>; my $status = 0; close $file; open($file, '>', "$filename"); for my $line (@content) { $status += $line =~ s/($what).*/$1 $newval/g; print $file $line; } print $file "$what $newval\n" if $status == 0; close $file; return $status; } #(filename, optionname, what we'll change, newval) #useful for files with options: OPT="some thing to change" # Does non-destructive change of one part of that string sub modify_config { my ($self, $filename, $option, $what, $newvalue) = @_; open(my $file, '<', "$filename"); my @content = <$file>; my $status = 0; close $file; open($file, '>', "$filename"); for my $line (@content) { $status += $line =~ s/^($option)(.*)$what(.*)/$1$2$newvalue$3/g; print $file $line; } close $file; return $status; } sub get_config #(filename, searchstring) { my ($self, $filename, $what) = @_; $what ||= ""; open(my $file, '<', "$filename"); my @content = <$file>; close $file; for my $line (@content) { if ($line =~ s/($what)\s*=*\s*(.+)/$2/) { chomp $line; return $line; } } return 0; } sub lvcreate{ my ($self, $name, $vg, $size) = @_; my $subname = (caller(0))[3]; if (!defined($name) || !defined($vg) || !defined(!$size)) { die "$subname: not enough information to create array\n"; } print "Creating root logical volume: /dev/$vg/$name\n"; system("lvcreate -L $size -n $name $vg 1>/dev/null"); } sub mkfs{ my ($self, $fstype, $dev_name, $opts) = @_; my $subname = (caller(0))[3]; $opts ||= ""; if (!defined($fstype) || !defined($dev_name)) { die "$subname: not enough information to create fs\n"; } if ( ! -e "$dev_name" ) { die "$subname: device doesn't exists\n"; } my $msg = ""; if ($opts ne "") { $msg = " with options $opts"; } print "Creating $fstype FS$msg: $dev_name\n"; system("yes | mkfs.$fstype $dev_name $opts 1>/dev/null"); } sub umount #(mounted_path) { my ($self, $mounted_path) = @_; die "Nothing to umount\n" if (!defined($mounted_path)); system("umount $mounted_path"); } sub cidr2ip #(cidr_bits) { my ($self, $bits) = @_; $bits = 2**32 - 2**(32-$bits); return join('.', unpack('C4', pack('N', $bits))); } sub new { my $class = shift; my $self = {}; bless $self, $class; return $self; } 1; __END__ =head1 AUTHOR Anatoly Burtsev, Eanatolyburtsev@yandex.ruE Pavel Potapenkov, Eppotapenkov@gmail.comE Vladimir Smirnov, Ecivil.over@gmail.comE =head1 COPYRIGHT AND LICENSE Copyright (C) 2011 by Anatoly Burtsev, Pavel Potapenkov, Vladimir Smirnov This library is free software; you can redistribute it and/or modify it under the same terms of GPL v2 or later, or, at your opinion under terms of artistic license. =cut lxctl-0.3.1+debian/LxctlHelpers/SSH.pm0000644000175000017500000000246011652614527017736 0ustar sysadminsysadminpackage LxctlHelpers::SSH; use strict; use warnings; my $ssh_; my $_ssh_user; my $_ssh_server; my $_ssh_port; sub connect { my $self = shift; ($_ssh_server, $_ssh_user, $_ssh_port) = @_; return $self; } sub execute { my ($self, $cmd) = @_; (system("ssh -p $_ssh_port $_ssh_user\@$_ssh_server '$cmd'") == 0) or die "Failed to remotely execute '$cmd'.\n"; } sub get_file { my ($self, $from, $to) = @_; print "Copying local $from to remote $to...\n"; (system("scp -r -P $_ssh_port $_ssh_user\@$_ssh_server:$from $to") == 0) or die "Failed to copy.\n\n"; return 1; } sub put_file { my ($self, $from, $to) = @_; print "Copying local $from to remote $to...\n"; (system("scp -r -P $_ssh_port $from $_ssh_user\@$_ssh_server:$to") == 0) or die "Failed to copy.\n\n"; return 1; } 1; __END__ =head1 AUTHOR Anatoly Burtsev, Eanatolyburtsev@yandex.ruE Pavel Potapenkov, Eppotapenkov@gmail.comE Vladimir Smirnov, Ecivil.over@gmail.comE =head1 COPYRIGHT AND LICENSE Copyright (C) 2011 by Anatoly Burtsev, Pavel Potapenkov, Vladimir Smirnov This library is free software; you can redistribute it and/or modify it under the same terms of GPL v2 or later, or, at your opinion under terms of artistic license. =cut lxctl-0.3.1+debian/LxctlHelpers/config.pm0000644000175000017500000001024011652614527020541 0ustar sysadminsysadminpackage LxctlHelpers::config; use strict; use warnings; use YAML::Tiny qw(DumpFile LoadFile); use Lxc::object; # @main_config_paths should be hardcoded, at least for now. my @main_config_paths = ("/etc/lxctl", "/etc", "."); sub print_warn #(message) { use Term::ANSIColor; my ($self, $message) = @_; print STDERR color 'bold yellow'; print STDERR "Warning:"; print STDERR color 'reset'; print STDERR " $message"; return 1; } sub load_main { my ($self) = @_; foreach my $path (@main_config_paths) { if ( -f "$path/lxctl.yaml" ) { my $yaml = YAML::Tiny->new; $yaml = YAML::Tiny->read("$path/lxctl.yaml"); # For compatibility reasons. Remove somewhere in 0.3+ eval { $self->{'lxc'}->set_roots_path($yaml->[0]->{'paths'}->{'ROOTS_PATH'}); $self->print_warn("$path/lxctl.yaml: ROOTS_PATH is deprecated. Please rename to ROOT_MOUNT_PATH\n"); } or do { $self->{'lxc'}->set_root_mount_path($yaml->[0]->{'paths'}->{'ROOT_MOUNT_PATH'}); }; eval { $self->{'lxc'}->set_config_path($yaml->[0]->{'paths'}->{'CONFIG_PATH'}); $self->print_warn("$path/lxctl.yaml: CONFIG_PATH is deprecated. Please rename to YAML_CONFIG_PATH\n"); } or do { $self->{'lxc'}->set_yaml_config_path($yaml->[0]->{'paths'}->{'YAML_CONFIG_PATH'}); }; $self->{'lxc'}->set_lxc_conf_dir($yaml->[0]->{'paths'}->{'LXC_CONF_DIR'}); $self->{'lxc'}->set_lxc_log_path($yaml->[0]->{'paths'}->{'LXC_LOG_PATH'}); $self->{'lxc'}->set_lxc_log_level($yaml->[0]->{'paths'}->{'LXC_LOG_LEVEL'}); $self->{'lxc'}->set_template_path($yaml->[0]->{'paths'}->{'TEMPLATE_PATH'}); $self->{'lxc'}->set_vg($yaml->[0]->{'lvm'}->{'VG'}); my $skip_check = $yaml->[0]->{'check'}->{'skip_kernel_config_check'}; if (defined($skip_check)) { $self->{'lxc'}->set_conf_check($skip_check); } last; } } return; } sub get_option_from_main #($section, $option_name) { my ($self, $section, $option_name) = @_; my $result = ''; foreach my $path (@main_config_paths) { if ( -f "$path/lxctl.yaml" ) { my $yaml = YAML::Tiny->new; $yaml = YAML::Tiny->read("$path/lxctl.yaml"); eval { $result = $yaml->[0]->{"$section"}->{"$option_name"}; }; last; } } return $result; } sub get_option_from_yaml #($filepath, $section, $option_name) { my ($self, $filepath, $section, $option_name) = @_; my $result = undef; return $result if (! -f "$filepath"); my $yaml = YAML::Tiny->new; $yaml = YAML::Tiny->read("$filepath"); eval { if ($section ne '') { $result = $yaml->[0]->{"$section"}->{"$option_name"}; } else { $result = $yaml->[0]->{"$option_name"}; } 1; }; return $result; } # hash_ref, filename # ex: $config->save_hash(\%options, "abrakadabra.yaml"); sub save_hash { my $self = shift; my $hash = shift; my $filename = shift; my %rhash = %$hash; DumpFile($filename, \%rhash); return; } # only arg: filename # ex: # my $tmp = $config->load_file("abrakadabra.yaml"); # my %opts = %$tmp; sub load_file { my $self = shift; my $filename = shift; my $hash = LoadFile($filename); my %h = %$hash; $h{'api_ver'} = 0 if (!defined($h{'api_ver'})); $hash = \%h; return $hash } #Current config API version sub get_api_ver { my $self = shift; return 1; } # Loads hash from file, then modifys it whti hash from 1-st arg # After that writes back to file. sub change_hash { my $self = shift; my $hash = shift; my $filename = shift; if ( ! -f $filename ) { return; } my $tmp = $self->load_file($filename); my %tmp_hash = %$tmp; my %rhash = %$hash; foreach my $key (sort keys %rhash) { $tmp_hash{$key} = $rhash{$key}; } $self->save_hash(\%tmp_hash, $filename); return; } sub new { my $class = shift; my $self = {}; bless $self, $class; $self->{'lxc'} = new Lxc::object; return $self; } 1; __END__ =head1 AUTHOR Anatoly Burtsev, Eanatolyburtsev@yandex.ruE Pavel Potapenkov, Eppotapenkov@gmail.comE Vladimir Smirnov, Ecivil.over@gmail.comE =head1 COPYRIGHT AND LICENSE Copyright (C) 2011 by Anatoly Burtsev, Pavel Potapenkov, Vladimir Smirnov This library is free software; you can redistribute it and/or modify it under the same terms of GPL v2 or later, or, at your opinion under terms of artistic license. =cut lxctl-0.3.1+debian/Makefile0000644000175000017500000000337611652614527016001 0ustar sysadminsysadmininstall: install -D lxctl $(DESTDIR)/usr/bin/lxctl install -D lxctl.yaml $(DESTDIR)/etc/lxctl/lxctl.yaml install -D bash_completion.d/lxctl $(DESTDIR)/etc/bash_completion.d/lxctl install -D bash_completion.d/lxctl_create $(DESTDIR)/etc/bash_completion.d/lxctl_create install -D bash_completion.d/lxctl_destroy $(DESTDIR)/etc/bash_completion.d/lxctl_destroy install -D bash_completion.d/lxctl_enter $(DESTDIR)/etc/bash_completion.d/lxctl_enter install -D bash_completion.d/lxctl_freeze $(DESTDIR)/etc/bash_completion.d/lxctl_freeze install -D bash_completion.d/lxctl_list $(DESTDIR)/etc/bash_completion.d/lxctl_list install -D bash_completion.d/lxctl_migrate $(DESTDIR)/etc/bash_completion.d/lxctl_migrate install -D bash_completion.d/lxctl_mount $(DESTDIR)/etc/bash_completion.d/lxctl_mount install -D bash_completion.d/lxctl_restart $(DESTDIR)/etc/bash_completion.d/lxctl_restart install -D bash_completion.d/lxctl_set $(DESTDIR)/etc/bash_completion.d/lxctl_set install -D bash_completion.d/lxctl_start $(DESTDIR)/etc/bash_completion.d/lxctl_start install -D bash_completion.d/lxctl_stop $(DESTDIR)/etc/bash_completion.d/lxctl_stop install -D bash_completion.d/lxctl_unfreeze $(DESTDIR)/etc/bash_completion.d/lxctl_unfreeze install -D bash_completion.d/lxctl_pid $(DESTDIR)/etc/bash_completion.d/lxctl_pid install -D bash_completion.d/lxctl_vz2lxc $(DESTDIR)/etc/bash_completion.d/lxctl_vz2lxc install -d $(DESTDIR)/usr/lib/perl/5.10/Lxc install -d $(DESTDIR)/usr/lib/perl/5.10/Lxctl install -d $(DESTDIR)/usr/lib/perl/5.10/LxctlHelpers install -d $(DESTDIR)/var/lxc/templates install -d $(DESTDIR)/var/lxc/root cp Lxc/* $(DESTDIR)/usr/lib/perl/5.10/Lxc cp Lxctl/* $(DESTDIR)/usr/lib/perl/5.10/Lxctl cp LxctlHelpers/* $(DESTDIR)/usr/lib/perl/5.10/LxctlHelpers lxctl-0.3.1+debian/Lxctl/0000755000175000017500000000000011652614535015415 5ustar sysadminsysadminlxctl-0.3.1+debian/Lxctl/create.pm0000644000175000017500000002366511652614527017233 0ustar sysadminsysadminpackage Lxctl::create; use strict; use warnings; use autodie qw(:all); use Getopt::Long qw(GetOptionsFromArray); use Lxc::object; use Lxctl::set; use LxctlHelpers::config; use LxctlHelpers::helper; use Data::UUID; use File::Path; my $config = new LxctlHelpers::config; my $helper = new LxctlHelpers::helper; my %options = (); my $lxc; my $yaml_conf_dir; my $lxc_conf_dir; my $root_mount_path; my $templates_path; my $vg; my @args; sub check_existance { my $self = shift; die "Container lxc conf directory $lxc_conf_dir/$options{'contname'} already exists!\n\n" if -e "$lxc_conf_dir/$options{'contname'}"; die "Container root directory $root_mount_path/$options{'contname'} already exists!\n\n" if -e "$root_mount_path/$options{'contname'}"; die "Container root logical volume /dev/$vg/$options{'contname'} already exists!\n\n" if -e "/dev/$vg/$options{'contname'}"; if ($options{'empty'} == 0) { if (! -e "$templates_path/$options{'ostemplate'}.tar.gz") { die "There is no such template: $templates_path/$options{'ostemplate'}.tar.gz\n\n"; } } return; } sub create_root { my $self = shift; if ($options{'rootsz'} ne 'share') { if (lc($options{'roottype'}) eq 'lvm') { $helper->lvcreate($options{'contname'}, $vg, $options{'rootsz'}); $helper->mkfs($options{'fs'}, "/dev/$vg/$options{'contname'}", $options{'mkfsopts'}); } elsif (lc($options{'roottype'}) eq 'file') { print "Creating root in file: $root_mount_path/$options{'contname'}.raw\n"; my $bs = 4096; my $count = $lxc->convert_size($options{'rootsz'}, 'b')/$bs; # Creating empty file of desired size. It's a bit slower then system dd, but still rather fast (around 10% slower then dd) system("dd if=/dev/zero of=$root_mount_path/$options{'contname'}.raw bs=$bs count=$count"); # open my $raw_file, '>' , "$root_mount_path/$options{'contname'}.raw"; # print $raw_file "\0" x($count*$bs); # close ($raw_file); $helper->mkfs($options{'fs'}, "$root_mount_path/$options{'contname'}.raw", $options{'mkfsopts'}); } } print "Creating directory: $root_mount_path/$options{'contname'}\n"; mkpath("$root_mount_path/$options{'contname'}/rootfs"); if ($options{'rootsz'} ne 'share') { print "Fixing fstab...\n"; my $what_to_mount = ""; my $additional_opts = ""; if (lc($options{'roottype'}) eq 'lvm') { $what_to_mount = "/dev/$vg/$options{'contname'}"; } elsif (lc($options{'roottype'}) eq 'file') { $what_to_mount = "$root_mount_path/$options{'contname'}.raw"; $additional_opts=",loop"; } # TODO: We disscused and decieded to keep all mounts in array of hashes in yaml file and apply on start. my %root_mp = ( 'from' => "$what_to_mount", 'to' => "$root_mount_path/$options{'contname'}", 'fs' => "$options{'fs'}", 'opts' => "$options{'mountoptions'}$additional_opts", ); $options{'rootfs_mp'} = \%root_mp; print "Mounting FS...\n"; system("mount -t $root_mp{'fs'} -o $root_mp{'opts'} $root_mp{'from'} $root_mp{'to'} 1>/dev/null"); } return; } sub check_create_options { my $self = shift; $Getopt::Long::passthrough = 1; GetOptions(\%options, 'ipaddr=s', 'hostname=s', 'ostemplate=s', 'config=s', 'roottype=s', 'root=s', 'rootsz=s', 'netmask|mask=s', 'defgw|gw=s', 'dns=s', 'macaddr=s', 'autostart=s', 'empty!', 'save!', 'load=s', 'debug', 'searchdomain=s', 'tz=s', 'fs=s', 'mkfsopts=s', 'mountoptions=s', 'mtu=i', 'userpasswd=s', 'pkgopt=s', 'addpkg=s', 'ifname=s'); if (defined($options{'load'})) { if ( ! -f $options{'load'}) { print "Cannot find config-file $options{'load'}, ignoring...\n"; last; }; my $result = $config->load_file($options{'load'}); my %opts_new = %$result; foreach my $key (sort keys %options) { $opts_new{$key} = $options{$key}; }; %options = %opts_new; } if (!defined($options{'contname'})) { die "No container name specified\n\n"; } my $ug = new Data::UUID; $options{'uuid'} = $ug->create_str(); $options{'ostemplate'} ||= $config->get_option_from_main('os', 'OS_TEMPLATE'); $options{'config'} ||= "$lxc_conf_dir/$options{'contname'}"; $options{'root'} ||= "$root_mount_path/$options{'contname'}"; $options{'rootsz'} ||= $config->get_option_from_main('root', 'ROOT_SIZE'); $options{'autostart'} ||= "1"; $options{'roottype'} ||= $config->get_option_from_main('root', 'ROOT_TYPE'); if (!defined($options{'empty'})) { $options{'empty'} = 0; } $options{'debug'} ||= 0; if (!defined($options{'save'})) { $options{'save'} = 1; } if ($options{'empty'} == 0) { $options{'ipaddr'} || print "You did not specify IP address! Using default.\n"; if (! $options{'ipaddr'} =~ m/\d+\.\d+\.\d+\.\d+\/\d+/ ) { $options{'netmask'} || print "You did not specify network mask! Using default.\n"; } $options{'defgw'} || print "You did not specify default gateway! Using default.\n"; $options{'dns'} || print "You did not specify DNS! Using default.\n"; } my @domain_tokens = split(/\./, $options{'contname'}); my $tmp_hostname = shift @domain_tokens; $options{'hostname'} ||= $tmp_hostname; $options{'searchdomain'} ||= join '.', @domain_tokens; if ($options{'searchdomain'} eq "") { $options{'searchdomain'} = $config->get_option_from_main('set', 'SEARCHDOMAIN'); } if ($options{'debug'}) { foreach my $key (sort keys %options) { print "options{$key} = $options{$key} \n"; }; } $options{'fs'} ||= $config->get_option_from_main('fs', 'FS'); $options{'mkfsopts'} ||= $config->get_option_from_main('fs', 'FS_OPTS'); $options{'mountoptions'} ||= $config->get_option_from_main('fs', 'FS_MOUNT_OPTS'); return $options{'uuid'}; } sub deploy_template { my $self = shift; my $template = "$templates_path/$options{'ostemplate'}.tar.gz"; print "Deploying template: $template\n"; system("tar xf $template -C $root_mount_path/$options{'contname'} 1>/dev/null"); return; } sub create_lxc_conf { my $self = shift; print "Creating lxc configuration file: $lxc_conf_dir/$options{'contname'}/config\n"; mkpath("$lxc_conf_dir/$options{'contname'}"); my $conf = "\ lxc.utsname = $options{'contname'} lxc.tty = 4 lxc.pts = 1024 lxc.rootfs = $root_mount_path/$options{'contname'}/rootfs lxc.mount = $lxc_conf_dir/$options{'contname'}/fstab lxc.cgroup.devices.deny = a # /dev/null and zero lxc.cgroup.devices.allow = c 1:3 rwm lxc.cgroup.devices.allow = c 1:5 rwm # consoles lxc.cgroup.devices.allow = c 5:1 rwm lxc.cgroup.devices.allow = c 5:0 rwm lxc.cgroup.devices.allow = c 4:0 rwm lxc.cgroup.devices.allow = c 4:1 rwm # /dev/{,u}random lxc.cgroup.devices.allow = c 1:9 rwm lxc.cgroup.devices.allow = c 1:8 rwm lxc.cgroup.devices.allow = c 136:* rwm lxc.cgroup.devices.allow = c 5:2 rwm # rtc lxc.cgroup.devices.allow = c 254:0 rwm lxc.network.type = veth lxc.network.flags = up lxc.network.link = br0 lxc.network.name = eth0 lxc.network.mtu = 1500 "; my $fstab = "\ proc $root_mount_path/$options{'contname'}/rootfs/proc proc nodev,noexec,nosuid 0 0 sysfs $root_mount_path/$options{'contname'}/rootfs/sys sysfs defaults 0 0 "; open my $config_file, '>', "$lxc_conf_dir/$options{'contname'}/config"; print $config_file $conf; close($config_file); open my $fstab_file, '>', "$lxc_conf_dir/$options{'contname'}/fstab"; print $fstab_file $fstab; close($fstab_file); return; } sub create_ssh_keys { my $self = shift; print "Regenerating SSH keys...\n"; eval { system("rm $root_mount_path/$options{'contname'}/rootfs/etc/ssh/ssh_host_*"); 1; } or do { print "Failed to delete old ssh keys!\n\n"; }; system("ssh-keygen -q -t rsa -f $root_mount_path/$options{'contname'}/rootfs/etc/ssh/ssh_host_rsa_key -N ''"); system("ssh-keygen -q -t dsa -f $root_mount_path/$options{'contname'}/rootfs/etc/ssh/ssh_host_dsa_key -N ''"); } sub deploy_packets { my $self = shift; defined($options{'addpkg'}) or return; $options{'pkgopt'} ||= ""; $options{'addpkg'} =~ s/,/ /g; print "Adding packages: $options{'addpkg'}\n"; ## Deb only system("chroot $root_mount_path/$options{'contname'}/rootfs/ apt-get $options{'pkgopt'} install $options{'addpkg'}"); return; } sub do { my ($self) = shift; $options{'contname'} = $_[0] or die "Name the container please!\n\n"; if ( $options{'contname'} =~ m/^-/ ) { print "Command specified instead of container name, trying to parse...\n"; undef($options{'contname'}); } else { shift; } $self->check_create_options(); $self->check_existance(); print "Creating container $options{'contname'}...\n"; $self->create_root(); $self->create_lxc_conf(); if (!defined($options{'ifname'})) { eval { $options{'ifname'} = $config->get_option_from_main('set', "IFNAME"); 1; } or do { $options{'ifname'} = "mac"; }; } my $setter = Lxctl::set->new(%options); $setter->set_macaddr(); if ($options{'empty'} == 0) { $self->deploy_template(); $self->create_ssh_keys(); $setter->set_ipaddr(); $setter->set_netmask(); $setter->set_defgw(); $setter->set_dns(); $setter->set_hostname(); $setter->set_searchdomain(); $setter->set_tz(); $setter->set_mtu(); $setter->set_userpasswd(); $setter->set_ifname(); $self->deploy_packets(); } $setter->set_autostart(); $options{'api_ver'} = $config->get_api_ver(); $options{'save'} && $config->save_hash(\%options, "$yaml_conf_dir/$options{'contname'}.yaml"); print "Container $options{'contname'}' created.\n"; return; } sub new { my $class = shift; my $self = {}; bless $self, $class; $lxc = new Lxc::object; $root_mount_path = $lxc->get_roots_path(); $templates_path = $lxc->get_template_path(); $yaml_conf_dir = $lxc->get_config_path(); $lxc_conf_dir = $lxc->get_lxc_conf_dir(); $vg = $lxc->get_vg(); return $self; } 1; __END__ =head1 AUTHOR Anatoly Burtsev, Eanatolyburtsev@yandex.ruE Pavel Potapenkov, Eppotapenkov@gmail.comE Vladimir Smirnov, Ecivil.over@gmail.comE =head1 COPYRIGHT AND LICENSE Copyright (C) 2011 by Anatoly Burtsev, Pavel Potapenkov, Vladimir Smirnov This library is free software; you can redistribute it and/or modify it under the same terms of GPL v2 or later, or, at your opinion under terms of artistic license. =cut lxctl-0.3.1+debian/Lxctl/stop.pm0000644000175000017500000000172311652614527016744 0ustar sysadminsysadminpackage Lxctl::stop; use strict; use warnings; use Lxc::object; my %options = (); sub do { my $self = shift; $options{'contname'} = shift or die "Name the container please!\n\n"; eval { $self->{'lxc'}->stop($options{'contname'}); print "It seems that \"$options{'contname'}\" is stopped now\n"; } or do { print "$@"; die "Cannot stop $options{'contname'}!\n\n"; }; return; } sub new { my $class = shift; my $self = {}; bless $self, $class; $self->{'lxc'} = Lxc::object->new; return $self; } 1; __END__ =head1 AUTHOR Anatoly Burtsev, Eanatolyburtsev@yandex.ruE Pavel Potapenkov, Eppotapenkov@gmail.comE Vladimir Smirnov, Ecivil.over@gmail.comE =head1 COPYRIGHT AND LICENSE Copyright (C) 2011 by Anatoly Burtsev, Pavel Potapenkov, Vladimir Smirnov This library is free software; you can redistribute it and/or modify it under the same terms of GPL v2 or later, or, at your opinion under terms of artistic license. =cut lxctl-0.3.1+debian/Lxctl/set.pm0000644000175000017500000002621611652614527016556 0ustar sysadminsysadminpackage Lxctl::set; use strict; use warnings; use autodie qw(:all); use Getopt::Long; use Digest::SHA qw(sha1_hex); use Linux::LVM; use Lxc::object; use LxctlHelpers::helper; use LxctlHelpers::config; my %options = (); my $yaml_conf_dir; my $lxc_conf_dir; my $root_mount_path; my $templates_path; my $vg; my $config = new LxctlHelpers::config; sub mac_create { my ($self, $data) = @_; my $mac = sha1_hex($data); $mac =~ s/(..)(..)(..)(..).*/F0:$1:$2:$3:$4/; return $mac; } sub set_hostname { my $self = shift; defined($options{'hostname'}) or return; print "Setting hostname: $options{'hostname'}\n"; open(my $hostname_file, '>', "$root_mount_path/$options{'contname'}/rootfs/etc/hostname"); seek $hostname_file,0,0; print $hostname_file $options{'hostname'}; close $hostname_file; my $searchdomain = $options{'searchdomain'}; if (!defined($options{'searchdomain'})) { if ( -e "$root_mount_path/$options{'contname'}/rootfs/etc/resolv.conf" ) { $searchdomain = $self->{'helper'}->get_config("$root_mount_path/$options{'contname'}/rootfs/etc/resolv.conf", 'search'); } else { $searchdomain = $config->get_option_from_main('set', 'SEARCHDOMAIN'); } } $self->{'helper'}->change_config("$root_mount_path/$options{'contname'}/rootfs/etc/hosts", '127.0.0.1', "$options{'hostname'}.$searchdomain $options{'hostname'} localhost"); return; } sub set_ipaddr { my $self = shift; defined($options{'ipaddr'}) or return; if ($options{'ipaddr'} =~ m/\d+\.\d+\.\d+\.\d+\/(\d+)/ ) { my $netmask = $self->{'helper'}->cidr2ip($1); $options{'netmask'} = $netmask; } print "Setting IP: $options{'ipaddr'}\n"; $self->{'helper'}->change_config("$root_mount_path/$options{'contname'}/rootfs/etc/network/interfaces", 'address', $options{'ipaddr'}); return; } sub set_macaddr { my $self = shift; if (defined($options{'macaddr'})) { print "Setting MAC: $options{'macaddr'}\n"; $self->{'lxc'}->set_conf($options{'contname'}, "lxc.network.hwaddr", $options{'macaddr'}); return; } defined($options{'contname'}) or return; my $mac = "01:" . $self->mac_create($options{'contname'}); print "Setting MAC: $mac\n"; $self->{'lxc'}->set_conf($options{'contname'}, "lxc.network.hwaddr", $mac); $options{'macaddr'} = $mac; return; } sub set_netmask { my $self = shift; defined($options{'netmask'}) or return; print "Setting netmask: $options{'netmask'}\n"; $self->{'helper'}->change_config("$root_mount_path/$options{'contname'}/rootfs/etc/network/interfaces", 'netmask', $options{'netmask'}); return; } sub set_mtu { my $self = shift; defined($options{'mtu'}) or return; print "Setting mtu: $options{'mtu'}\n"; $self->{'helper'}->change_config("$root_mount_path/$options{'contname'}/rootfs/etc/network/interfaces", 'mtu', $options{'mtu'}); $self->{'helper'}->change_config("$lxc_conf_dir/$options{'contname'}/config", 'lxc.network.mtu = ', $options{'mtu'}); return; } sub set_ifname { my $self = shift; defined($options{'ifname'}) or return; if ($options{'ifname'} eq "mac") { $options{'ifname'} = $options{'mac'}; if (!defined($options{'ifname'})) { $options{'ifname'} = $config->get_option_from_yaml("$yaml_conf_dir/$options{'contname'}.yaml", "", "macaddr"); } if (!defined($options{'ifname'})) { $options{'ifname'} = $self->{'lxc'}->get_conf($options{'contname'}, "lxc.network.hwaddr"); $options{'macaddr'} = $options{'ifname'}; } $options{'ifname'} =~ s/^..:(.*)/$1/; $options{'ifname'} =~ s/://g; $options{'ifname'} = "veth" . $options{'ifname'}; } elsif ($options{'ifname'} eq "ip") { if (!defined($options{'ipaddr'})) { $options{'ipaddr'} = $config->get_option_from_yaml("$yaml_conf_dir/$options{'contname'}.yaml", "", "ipaddr"); die "No IP address specified" if (!defined($options{'ipaddr'})); } $options{'ifname'} = $options{'ipaddr'}; $options{'ifname'} =~ s/\d+\.\d+\.(\d+).(\d+)/$1$2/g; $options{'ifname'} = "lxc" . $options{'ifname'}; } print "Setting interface (host part) name to $options{'ifname'}\n"; my $size = bytes::length($options{'ifname'}); die "Wow... that thing is big... too big for me! Maximum I know how to handle is 15 bytes\n" if ($size > 15); my $old_name; my $step = 1; eval { $old_name = $config->get_option_from_yaml("$yaml_conf_dir/$options{'contname'}.yaml", "", "ifname") or die; $step++; system("ip link set $old_name down"); $step++; system("ip link set $old_name name $options{'ifname'}"); $step++; system("ip link set $options{'ifname'} up"); $step++; 1; } or do { print "THIS IS NOT FATAL: Failed to do step $step, can't change ifname of interface in runtime. Please, restart container manualy.\n"; }; $self->{'helper'}->change_config("$lxc_conf_dir/$options{'contname'}/config", 'lxc.network.link', $options{'ifname'}); return; } sub set_defgw { my $self = shift; defined($options{'defgw'}) or return; print "Setting gateway: $options{'defgw'}\n"; $self->{'helper'}->change_config("$root_mount_path/$options{'contname'}/rootfs/etc/network/interfaces", 'gateway', $options{'defgw'}); return; } sub set_dns { my $self = shift; defined($options{'dns'}) or return; print "Setting DNS: $options{'dns'}\n"; $self->{'helper'}->change_config("$root_mount_path/$options{'contname'}/rootfs/etc/resolv.conf", 'nameserver', $options{'dns'}); return; } sub set_searchdomain { my $self = shift; defined($options{'searchdomain'}) or return; print "Setting search domain: $options{'searchdomain'}\n"; $self->{'helper'}->change_config("$root_mount_path/$options{'contname'}/rootfs/etc/resolv.conf", 'search', $options{'searchdomain'}); my $hostname = $self->{'helper'}->get_config("$root_mount_path/$options{'contname'}/rootfs/etc/hostname", ""); $self->{'helper'}->change_config("$root_mount_path/$options{'contname'}/rootfs/etc/hosts", '127.0.0.1', "$hostname.$options{'searchdomain'} $hostname localhost"); return; } sub set_userpasswd { my $self = shift; defined($options{'userpasswd'}) or return; print "Setting password for user: $options{'userpasswd'}\n"; die "Failed to change password!\n\n" if system("echo '$options{'userpasswd'}' | chroot $root_mount_path/$options{'contname'}/rootfs/ chpasswd"); return; } sub set_rootsz { my $self = shift; defined($options{'rootsz'}) or return; $options{'roottype'} ||= 'lvm'; if (lc($options{'roottype'}) eq 'file') { print "set rootsz is unsupported for root in file\n\n"; return; } $options{'rootsz'} =~ m/^[+-]?\d+[.]?\d*[bBsSkKmMgGtTpPeE]/ or die "Bad size!"; my %lvm_info = get_lv_info("/dev/$vg/$options{'contname'}"); my $desired_size; my $tmp = $lvm_info{'size'}; if ($options{'rootsz'} =~ m/^[+](.+)+/) { print "SET: $1\n"; $desired_size = $self->{'lxc'}->convert_size($1, $lvm_info{'size_unit'}, 0); $desired_size += $lvm_info{'size'}; } elsif ($options{'rootsz'} =~ m/^-(.*)+/) { print "Shrinking is not supported yet\n"; $desired_size = $self->{'lxc'}->convert_size($1, $lvm_info{'size_unit'}, 0); $desired_size = $lvm_info{'size'} - $desired_size; if ($desired_size <= 0) { print "Can't resize this much!\n"; return; } } else { $desired_size = $self->{'lxc'}->convert_size($options{'rootsz'}, $lvm_info{'size_unit'}, 0); } if ($desired_size == $lvm_info{'size'}) { print "Already desired size, exiting...\n"; return; } elsif ($desired_size < $lvm_info{'size'}) { print "Shrinking is not supported yet\n"; return; } $desired_size .= $lvm_info{'size_unit'}; $options{'rootsz'} = $desired_size; print "Setting root size: $desired_size\n"; (system("lvextend -L $desired_size /dev/$vg/$options{'contname'}") == 0) or die "Failed to extend logical volume.\n\n"; (system("resize2fs /dev/$vg/$options{'contname'}") == 0) or die "Failed to resize filesystem.\n\n"; return; } sub set_cgroup { my $self = shift; my ($name, $value) = @_; defined($options{$name}) or return; print "Setting $name: $options{$name}\n"; # Commenting out for now. cpu.shares can be any val # $options{$name} =~ m/^\d+$/ or # die "Bad $name option!\n\n"; eval { $self->{'lxc'}->set_cgroup($options{'contname'}, $value, $options{$name}, 1); $self->{'lxc'}->set_conf($options{'contname'}, "lxc.cgroup." . $value, $options{$name}); } or do { print "$@"; die "Failed to change $name share!\n\n"; }; return; } sub set_autostart { my $self = shift; defined($options{'autostart'}) or return; my $autostart = $options{'autostart'}; my $name = $options{'contname'}; if ($autostart == 0) { print "Removing $name from autostart\n"; $self->{'helper'}->modify_config("/etc/default/lxc", "CONTAINERS", $name, ""); } else { print "Adding $name to autostart\n"; $self->{'helper'}->modify_config("/etc/default/lxc", "CONTAINERS", "\"\$", " $name\""); } } sub set_tz() { use File::Copy "cp"; my $self = shift; defined($options{'tz'}) or return; print "Setting timesone: $options{'tz'}...\n"; my $cont_root_path = "$root_mount_path/$options{'contname'}/rootfs"; -e "$cont_root_path/usr/share/zoneinfo/$options{'tz'}" or die "No such timezone: $options{'tz'}!\n\n"; cp("$cont_root_path/usr/share/zoneinfo/$options{'tz'}", "$cont_root_path/etc/localtime"); } sub do { my $self = shift; $options{'contname'} = shift or die "Name the container please!\n\n"; die "Strange, rare name... I don't know how to deal with it.\n" if ($options{'contname'} =~ m/^-.*/); GetOptions(\%options, 'ipaddr=s', 'hostname=s', 'userpasswd=s', 'nameserver=s', 'searchdomain=s', 'rootsz=s', 'netmask|mask=s', 'defgw|gw=s', 'dns=s', 'cpus=s', 'cpu-shares=s', 'mem=s', 'io=s', 'macaddr=s', 'autostart=s', 'tz|timezone=s', 'mtu=i', 'ifname=s'); if (defined($options{'mem'})) { $options{'mem'} = $self->{'lxc'}->convert_size($options{'mem'}, "B"); } # Dirty hack. set_macaddr used from create and should be able to work without --maccaddr option. $self->set_macaddr() if defined($options{'macaddr'}); $self->set_ipaddr(); $self->set_netmask(); $self->set_defgw(); $self->set_dns(); $self->set_hostname(); $self->set_searchdomain(); $self->set_userpasswd(); $self->set_rootsz(); $self->set_autostart(); $self->set_tz(); $self->set_mtu(); $self->set_ifname(); $self->set_cgroup('cpu-shares', 'cpu.shares'); $self->set_cgroup('cpus', 'cpuset.cpus'); $self->set_cgroup('mem', 'memory.limit_in_bytes'); $self->set_cgroup('io', 'blkio.weight'); $config->change_hash(\%options, "$yaml_conf_dir/$options{'contname'}.yaml"); return; } sub new { my $class = shift; my $self = {}; bless $self, $class; $self->{'lxc'} = Lxc::object->new; $self->{'helper'} = LxctlHelpers::helper->new; $root_mount_path = $self->{'lxc'}->get_roots_path(); $templates_path = $self->{'lxc'}->get_template_path(); $yaml_conf_dir = $self->{'lxc'}->get_config_path(); $lxc_conf_dir = $self->{'lxc'}->get_lxc_conf_dir(); $vg = $self->{'lxc'}->get_vg(); %options = @_; return $self; } 1; __END__ =head1 AUTHOR Anatoly Burtsev, Eanatolyburtsev@yandex.ruE Pavel Potapenkov, Eppotapenkov@gmail.comE Vladimir Smirnov, Ecivil.over@gmail.comE =head1 COPYRIGHT AND LICENSE Copyright (C) 2011 by Anatoly Burtsev, Pavel Potapenkov, Vladimir Smirnov This library is free software; you can redistribute it and/or modify it under the same terms of GPL v2 or later, or, at your opinion under terms of artistic license. =cut lxctl-0.3.1+debian/Lxctl/list.pm0000644000175000017500000001265611652614527016741 0ustar sysadminsysadminpackage Lxctl::list; use strict; use warnings; use Getopt::Long; use Lxc::object; use LxctlHelpers::getters; use LxctlHelpers::config; use Data::UUID; use List::Util qw[max]; my $getters = new LxctlHelpers::getters; my %sizes; my $sep = " "; my $config_reader = new LxctlHelpers::config; my @vms; sub get_cpus { my ($self, $name) = @_; my $cpu_cnt = 0; eval { my $cpus = $self->{lxc}->get_cgroup($name, "cpuset.cpus"); my @splited = split(/,/, $cpus); foreach my $part (@splited) { if ($part =~ m/(\d+)-(\d+)/) { $cpu_cnt += (int($2) - int($1) + 1); } else { $cpu_cnt += int($part); } } 1; } or do { $cpu_cnt = 2; }; return $cpu_cnt; } sub get_all_info { my $self = shift; my @vms_result; my $cnt = 0; my $lxc_conf_dir = $self->{lxc}->get_lxc_conf_dir(); my $yaml_conf_dir = $self->{lxc}->get_yaml_config_path(); my $vm_option_ref; my %vm_option; foreach my $vm (@vms) { my %info; my $root_path; my $ghost = 0; $vm_option_ref = $config_reader->load_file("$yaml_conf_dir/$vm.yaml"); %vm_option = %$vm_option_ref; eval { $root_path = $self->{lxc}->get_conf($vm, "lxc.rootfs"); } or do { $root_path = "N/A"; $ghost = 1; }; if (!defined($vm_option{'uuid'})) { my $ug = new Data::UUID; $vm_option{'uuid'} = $ug->create_str(); $config_reader->save_hash(\%vm_option, "$yaml_conf_dir/$vm.yaml"); } $info{'uuid'} = $vm_option{'uuid'}; $info{'mac'} = uc($self->{helper}->get_config($lxc_conf_dir."/$vm/config", "lxc.network.hwaddr")); if ($vm_option{'rootsz'} ne 'share') { $info{'disksize_mb'} = int($self->{lxc}->convert_size($vm_option{'rootsz'}, "MiB", 0)); } else { $info{'disksize_mb'} = 'SHARED'; } $info{'template'} = $vm_option{'ostemplate'}; $vm_option{'mem'} ||= "1EB"; $info{'memory_mb'} = int($self->{lxc}->convert_size($vm_option{'mem'}, "MiB", 0)); $info{'name'} = $vm_option{'contname'}; $info{'cpus'} = $self->get_cpus($vm_option{'contname'}); eval { $info{'cpu'} = chomp($self->{lxc}->get_cgroup($vm, "cpuset.cpus")); 1; } or do { # By default pid will got all CPUs $info{'cpu'} = "All"; }; eval { $info{'cpushares'} = chomp($self->{lxc}->get_cgroup($vm, "cpu.shares")); 1; } or do { $info{'cpushares'} = 1024; }; eval { my $ip = $getters->get_ip($vm); ($info{'ip'}) = $ip =~ m/(\d+\.\d+\.\d+\.\d+)/; 1; } or do { $info{'ip'} = "N/A"; }; eval { $info{'hostname'} = $self->{lxc}->get_conf($vm, "lxc.utsname"); 1; } or do { $info{'hostname'} = "N/A"; }; if ($ghost == 0) { my $df = `df -B 1 $root_path`; my ($total, $free) = $df =~ m/\s+(\d+)\s+\d+\s+(\d+)/g; if (defined($total)) { $info{'disk_total_mb'} = sprintf("%.2f", $self->{lxc}->convert_size($total, 'MiB', 0)); $info{'disk_free_mb'} = sprintf("%.2f", $self->{lxc}->convert_size($free, 'MiB', 0)); } else { $info{'disk_total_mb'} = "N/A"; $info{'disk_free_mb'} = "N/A"; } $info{'status'} = lc($self->{lxc}->status($vm)); } else { $info{'disk_total_mb'} = "N/A"; $info{'disk_free_mb'} = "N/A"; $info{'status'} = "ghost"; }; foreach my $key (sort keys %info) { if (!defined($sizes{$key}) || ($sizes{$key} < length($info{$key}))) { $sizes{$key} = max(length($info{$key}), length($key)); } } push(@vms_result, \%info); $cnt++; } return @vms_result; } sub do { my $self = shift; my $all; my $raw; my $columns; my $header_printed = 0; $Getopt::Long::passthrough = 1; my $vm_list = $ARGV[0]; undef $vm_list if ($vm_list && $vm_list =~ m/^-+/); GetOptions('columns=s' => \$columns, 'raw' => \$raw, 'all' => \$all, 'noheader' => \$header_printed); if ($raw) { @vms = $self->{lxc}->ls(); foreach my $vm (@vms) { print "$vm "; } print "\n"; return; } if (defined($vm_list)) { @vms = split(/,/, $vm_list); } else { @vms = $self->{lxc}->ls(); } $all ||= 0; $columns ||= $config_reader->get_option_from_main("list", "COLUMNS"); my @splitted = split(/,/, $columns); my @vms_new = $self->get_all_info(); my %vm_hash; my $tmp_string; if ($all) { foreach my $vm_ref (@vms_new) { %vm_hash = %$vm_ref; if ($header_printed == 0) { foreach my $key (sort keys %vm_hash) { $tmp_string = "%".$sizes{$key}."s$sep"; printf "$tmp_string", uc("$key"); } print "\n"; $header_printed = 1; } foreach my $key (sort keys %vm_hash) { printf "%".$sizes{$key}."s$sep", $vm_hash{$key}; } print "\n"; } } else { foreach my $vm_ref (@vms_new) { %vm_hash = %$vm_ref; if ($header_printed == 0) { foreach my $key (@splitted) { if (defined($vm_hash{$key})) { printf "%".$sizes{$key}."s$sep", uc("$key"); } } print "\n"; $header_printed = 1; } foreach my $key (@splitted) { if (defined($vm_hash{$key})) { printf "%".$sizes{$key}."s$sep", $vm_hash{$key}; } } print "\n"; } } return; } sub new { my $class = shift; my $self = {}; bless $self, $class; $self->{lxc} = new Lxc::object; $self->{helper} = new LxctlHelpers::helper; return $self; } 1; __END__ =head1 AUTHOR Anatoly Burtsev, Eanatolyburtsev@yandex.ruE Pavel Potapenkov, Eppotapenkov@gmail.comE Vladimir Smirnov, Ecivil.over@gmail.comE =head1 COPYRIGHT AND LICENSE Copyright (C) 2011 by Anatoly Burtsev, Pavel Potapenkov, Vladimir Smirnov This library is free software; you can redistribute it and/or modify it under the same terms of GPL v2 or later, or, at your opinion under terms of artistic license. =cut lxctl-0.3.1+debian/Lxctl/unfreeze.pm0000644000175000017500000000167511652614527017610 0ustar sysadminsysadminpackage Lxctl::unfreeze; use strict; use warnings; use Lxc::object; my %options = (); sub do { my $self = shift; $options{'contname'} = shift or die "Name the container please!\n\n"; eval { $self->{'lxc'}->unfreeze($options{'contname'}); } or do { print "$@"; die "Cannot unfreeze $options{'contname'}!\n\n"; }; print "Successfuly unfrozen!\n"; return; } sub new { my $class = shift; my $self = {}; bless $self, $class; $self->{'lxc'} = new Lxc::object; return $self; } 1; __END__ =head1 AUTHOR Anatoly Burtsev, Eanatolyburtsev@yandex.ruE Pavel Potapenkov, Eppotapenkov@gmail.comE Vladimir Smirnov, Ecivil.over@gmail.comE =head1 COPYRIGHT AND LICENSE Copyright (C) 2011 by Anatoly Burtsev, Pavel Potapenkov, Vladimir Smirnov This library is free software; you can redistribute it and/or modify it under the same terms of GPL v2 or later, or, at your opinion under terms of artistic license. =cut lxctl-0.3.1+debian/Lxctl/enter.pm0000644000175000017500000000175511652614527017101 0ustar sysadminsysadminpackage Lxctl::enter; use strict; use warnings; use Lxc::object; my %options = (); sub do { my $self = shift; $options{'contname'} = shift or die "Name the container please!\n\n"; eval { print "Warning! All processes you'll start from here will ignore cgroup limits!\n"; $self->{'lxc'}->attach($options{'contname'}); } or do { print "$@"; die "Cannot attach to $options{'contname'}!\n\n"; }; return; } sub new { my $class = shift; my $self = {}; bless $self, $class; $self->{'lxc'} = new Lxc::object; return $self; } 1; __END__ =head1 AUTHOR Anatoly Burtsev, Eanatolyburtsev@yandex.ruE Pavel Potapenkov, Eppotapenkov@gmail.comE Vladimir Smirnov, Ecivil.over@gmail.comE =head1 COPYRIGHT AND LICENSE Copyright (C) 2011 by Anatoly Burtsev, Pavel Potapenkov, Vladimir Smirnov This library is free software; you can redistribute it and/or modify it under the same terms of GPL v2 or later, or, at your opinion under terms of artistic license. =cut lxctl-0.3.1+debian/Lxctl/start.pm0000644000175000017500000001100611652614535017106 0ustar sysadminsysadminpackage Lxctl::start; use strict; use warnings; use Lxc::object; use LxctlHelpers::config; use File::Path; my $config = new LxctlHelpers::config; my %options = (); my $yaml_conf_dir; my $contname; my $root_path; my $lxc; my $lxc_conf_dir; my $lxc_log_path; my $lxc_log_level; sub _actual_start { my ($self, $daemon) = @_; my $lxc_real_log_path = $lxc_log_path; $lxc_real_log_path =~ s/%CONTNAME%/$contname/g; $lxc->start($contname, $daemon, $lxc_conf_dir."/".$contname."/config", $lxc_real_log_path, $lxc_log_level); } # At 0.3.0 we mount root from config at start. Make shure we have it there, not in fstab. sub check_root_in_config { my ($self, %vm_options) = @_; if (defined($vm_options{'api_ver'}) && $vm_options{'api_ver'} == $config->get_api_ver()) { die "$yaml_conf_dir/$contname.yaml has API version $vm_options{'api_ver'} (and current is ".$config->get_api_ver().") and has no root_mp statement. Fix it.\n\n"; } $vm_options{'api_ver'} = $config->get_api_ver(); open(my $fstab, '<', '/etc/fstab'); my @mpoints = <$fstab>; close $fstab; my $vg = $lxc->get_vg(); for my $mp (@mpoints) { next if !($mp =~ m/^\/dev\/$vg\/$vm_options{'contname'}/); my @fstab_line = split (/\s+/, $mp); my %root_mp = ('from' => $fstab_line[0], 'to' => $fstab_line[1], 'fs' => $fstab_line[2], 'opts' => $fstab_line[3]); $vm_options{'rootfs_mp'} = \%root_mp; $config->change_hash(\%vm_options, "$yaml_conf_dir/$contname.yaml"); chomp $mp; use Term::ANSIColor; print color "bold red"; print "Removing $mp from /etc/fstab.\nCHECK IT! Backup will be saved at /etc/fstab.bak.\n\n"; print color "reset"; system("sed -i.bak 's#$mp##' /etc/fstab"); return \%root_mp; } die "There is no root mount directions at $yaml_conf_dir/$contname.yaml and I failed to find them in /etc/fstab.\n\n"; } sub do { my $self = shift; $contname = shift or die "Name the container please!\n\n"; my $vm_option_ref; my %vm_options; $vm_option_ref = $config->load_file("$yaml_conf_dir/$contname.yaml"); %vm_options = %$vm_option_ref; my @mount_points; my $mount_result = `mount`; # mount root if (!defined($vm_options{'rootsz'}) || $vm_options{'rootsz'} ne 'share') { my $mp_ref = $vm_options{'rootfs_mp'}; $mp_ref = $self->check_root_in_config(%vm_options) if (!defined($mp_ref)); my %mp = %$mp_ref; # print "\n\n\nDEBUG: $mount_result\n$mp{'to'}\n\n\n"; # print "TRUE\n" if ($mount_result !~ m/^$mp{'from'}/); if ($mount_result !~ m/on $mp{'to'}/) { (system("mount -t $mp{'fs'} -o $mp{'opts'} $mp{'from'} $mp{'to'}") == 0) or die "Failed to mount $mp{'from'} to $mp{'to'}\n\n"; } } if (defined $vm_options{'mountpoints'}) { { my $mount_ref = $vm_options{'mountpoints'}; @mount_points = @$mount_ref; if ($#mount_points == -1 ) { print "No mount points specified!\n"; last; } #TODO: Move to mount module. foreach my $mp_ref (@mount_points) { my %mp = %$mp_ref; my $cmd = "mount"; my $to = quotemeta("$root_path/$contname/rootfs$mp{'to'}"); next if ($mount_result =~ /on $to/); if (defined($mp{'fs'})) { $cmd .= " -t $mp{'fs'}"; } mkpath("$to") if (! -e "$to"); $cmd .= " -o $mp{'opts'} $mp{'from'} $to"; system("$cmd"); } } } else { print "No mount points specified!\n"; } eval { $self->_actual_start(1); sleep(1); my $status = $lxc->status($contname); if ($status eq "STOPPED") { $self->_actual_start(0); } print "It seems that \"$contname\" was started.\n"; } or do { print "$@"; # We should check $@ to analyize some common errors, such as: # 1) No cgroups mounter # 2) if it tries to mount proc to /usr/lib/lxc//proc - it's usualy forgotten /proc in container's rootfs. Check same for dev and sys # 3) ... Maybe more in future. die "Cannot start $contname!\n\n"; }; return; } sub new { my $class = shift; my $self = {}; bless $self, $class; $lxc = Lxc::object->new; $yaml_conf_dir = $lxc->get_yaml_config_path(); $lxc_conf_dir = $lxc->get_lxc_conf_dir(); $root_path = $lxc->get_root_mount_path(); $lxc_log_path = $lxc->get_lxc_log_path(); $lxc_log_level = $lxc->get_lxc_log_level(); return $self; } 1; __END__ =head1 AUTHOR Anatoly Burtsev, Eanatolyburtsev@yandex.ruE Pavel Potapenkov, Eppotapenkov@gmail.comE Vladimir Smirnov, Ecivil.over@gmail.comE =head1 COPYRIGHT AND LICENSE Copyright (C) 2011 by Anatoly Burtsev, Pavel Potapenkov, Vladimir Smirnov This library is free software; you can redistribute it and/or modify it under the same terms of GPL v2 or later, or, at your opinion under terms of artistic license. =cut lxctl-0.3.1+debian/Lxctl/mount.pm0000644000175000017500000001206211652614527017117 0ustar sysadminsysadminpackage Lxctl::mount; use strict; use warnings; use autodie qw(:all); use Lxc::object; use LxctlHelpers::config; use Getopt::Long; use File::Path; use List::Util qw[max]; my %options = (); my $yaml_conf_dir; my $lxc; my $config = new LxctlHelpers::config; my $contname; my $root_path; sub add { my $self = shift; $Getopt::Long::passthrough = 1; GetOptions(\%options, 'from=s', 'to=s', 'mountoptions=s', 'fs=s'); $options{'from'} || die "Don't know what to mount.\n\n"; $options{'to'} || die "Don't know where to mount.\n\n"; -e $options{'from'} || die "You are trying to mount void. Lxctl does not able to do it. Yet.\n\n"; if (!defined($options{'mountoptions'})) { print "No options specified, using telepathy...\n"; if ( -d $options{'from'} ) { $options{'mountoptions'} = "bind"; } elsif ( -e $options{'from'} ) { $options{'mountoptions'} = "noatime"; } } if ($lxc->status($contname) eq "RUNNING") { my $cmd = "mount"; $cmd .= " -t $options{'fs'}" if defined($options{'fs'}); mkpath("$root_path/$contname/rootfs/$options{'to'}") if (! -e "$root_path/$contname/rootfs/$options{'to'}"); $cmd .= " -o $options{'mountoptions'} $options{'from'} $root_path/$contname/rootfs/$options{'to'}"; system("$cmd"); } my $vm_option_ref; my %vm_options; $vm_option_ref = $config->load_file("$yaml_conf_dir/$contname.yaml"); %vm_options = %$vm_option_ref; my @mount_points; if (defined $vm_options{'mountpoints'}) { my $mount_ref = $vm_options{'mountpoints'}; @mount_points = @$mount_ref; } push (@mount_points, \%options); $vm_options{'mountpoints'} = \@mount_points; $config->save_hash(\%vm_options, "$yaml_conf_dir/$contname.yaml"); return; } sub list { my $self = shift; my $vm_option_ref; my %vm_options; $vm_option_ref = $config->load_file("$yaml_conf_dir/$contname.yaml"); %vm_options = %$vm_option_ref; my @mount_points; if (defined $vm_options{'mountpoints'}) { my $mount_ref = $vm_options{'mountpoints'}; @mount_points = @$mount_ref; } else { print "No mountpoints\n"; return; } if ($#mount_points) { print "No mountpoints\n"; return; } my %mp; my $columns = "id,from,to,fs,mountoptions"; my %sizes; my $id_size = @mount_points; $sizes{'fs'} = length("auto"); $sizes{'id'} = max(length("$id_size"), length("id")); my $sep = " "; foreach my $mp_ref (@mount_points) { %mp = %$mp_ref; foreach my $key (split(/,/, $columns)) { if (defined($mp{$key}) && (!defined($sizes{$key}) || $sizes{$key} < length($mp{$key}))) { $sizes{$key} = length($mp{$key}); } } } foreach my $key (split(/,/, $columns)) { printf "%".$sizes{$key}."s$sep", $key; } print "\n"; my $cnt = 0; foreach my $mp_ref (@mount_points) { %mp = %$mp_ref; $cnt++; foreach my $key (split(/,/, $columns)) { $mp{'id'} = $cnt; if ($key eq "fs" && !defined($mp{$key})) { $mp{$key} = "auto"; } printf "%".$sizes{$key}."s$sep", $mp{$key}; } print "\n"; } return; } sub del { my $self = shift; my $id; $Getopt::Long::passthrough = 1; GetOptions('id=i' => \$id); die "No id specified, don't know what to delete.\n\n" if !defined($id); my $vm_option_ref; my %vm_options; $vm_option_ref = $config->load_file("$yaml_conf_dir/$contname.yaml"); %vm_options = %$vm_option_ref; my @mount_points; if (defined $vm_options{'mountpoints'}) { my $mount_ref = $vm_options{'mountpoints'}; @mount_points = @$mount_ref; } else { print "Nothing to delete\n"; return; } my @new_array; my $cnt = 0; foreach my $val (@mount_points) { $cnt++; if ($cnt != $id) { push(@new_array, $val) } elsif ($lxc->status($contname) eq "RUNNING") { my %mount = %$val; my $cmd = "umount -r -f $root_path/$contname/rootfs/$mount{'to'}"; eval { system("$cmd"); } or do { print "There was a problem while umounting: $@\n"; } } } $vm_options{'mountpoints'} = \@new_array; $config->save_hash(\%vm_options, "$yaml_conf_dir/$contname.yaml"); } sub do { my $self = shift; $contname = shift or die "Name the container please!\n\n"; $Getopt::Long::passthrough = 1; my $del; my $add; my $list; GetOptions('add' => \$add, 'del' => \$del, 'list' => \$list); my $opts = 0; $opts += 1 if $add; $opts += 1 if $del; $opts += 1 if $list; die "No action options specified. Avaliable: --add, --list or --del.\n\n" if $opts < 1; die "Specify only ONE of --add, --list or --del.\n\n" if $opts > 1; $self->add() if $add; $self->del() if $del; $self->list() if $list; return; } sub new { my $class = shift; my $self = {}; bless $self, $class; $lxc = Lxc::object->new; $yaml_conf_dir = $lxc->get_yaml_config_path(); $root_path = $lxc->get_root_mount_path; return $self; } 1; __END__ =head1 AUTHOR Anatoly Burtsev, Eanatolyburtsev@yandex.ruE Pavel Potapenkov, Eppotapenkov@gmail.comE Vladimir Smirnov, Ecivil.over@gmail.comE =head1 COPYRIGHT AND LICENSE Copyright (C) 2011 by Anatoly Burtsev, Pavel Potapenkov, Vladimir Smirnov This library is free software; you can redistribute it and/or modify it under the same terms of GPL v2 or later, or, at your opinion under terms of artistic license. =cut lxctl-0.3.1+debian/Lxctl/pid.pm0000644000175000017500000000364211652614527016535 0ustar sysadminsysadminpackage Lxctl::pid; use strict; use warnings; my %options = (); sub get_proc_name { my ($self, $pid) = @_; open(my $file, '<', "/proc/$pid/comm") or die "Failed to open /proc/$pid/comm.\n\n"; my $line = <$file>; chomp $line; return $line; } sub get_proc_container { my ($self, $pid) = @_; open(my $file, '<', "/proc/$pid/cgroup") or die "Failed to open /proc/$pid/cgroup.\n\n"; my $line = <$file>; $line =~ s/^\S+:\S+:\/(\S*)$/$1/; chomp $line; $line = "dom0 process" if $line eq ""; return $line; } sub get_longest_string { my ($self, @arr) = @_; my @tmp = sort { length($b) <=> length($a) } @arr; return length($tmp[0]); } sub do { my $self = shift; my $pidlist = shift or die "What pid[s] should I check?\n\n"; my @pids = ('PID'); my @names = ('NAME'); my @cts = ('CT_NAME'); my @tmp_pids = split(/,/, $pidlist); for my $pid (@tmp_pids) { $pid =~ m/^\d+$/ or die "$pid - that's not a pid. No-no-no-no, don't try to fool me.\n\n"; push(@pids, $pid); push(@names, $self->get_proc_name($pid)); push(@cts, $self->get_proc_container($pid)); } my $max_pids = $self->get_longest_string(@pids); my $max_names = $self->get_longest_string(@names); my $max_cts = $self->get_longest_string(@cts); for (my $i=0; $i < scalar(@pids); $i++) { printf " %".$max_pids."s %".$max_names."s %".$max_cts."s\n", $pids[$i], $names[$i], $cts[$i]; } return; } sub new { my $class = shift; my $self = {}; bless $self, $class; return $self; } 1; __END__ =head1 AUTHOR Anatoly Burtsev, Eanatolyburtsev@yandex.ruE Pavel Potapenkov, Eppotapenkov@gmail.comE Vladimir Smirnov, Ecivil.over@gmail.comE =head1 COPYRIGHT AND LICENSE Copyright (C) 2011 by Anatoly Burtsev, Pavel Potapenkov, Vladimir Smirnov This library is free software; you can redistribute it and/or modify it under the same terms of GPL v2 or later, or, at your opinion under terms of artistic license. =cut lxctl-0.3.1+debian/Lxctl/freeze.pm0000644000175000017500000000166511652614527017244 0ustar sysadminsysadminpackage Lxctl::freeze; use strict; use warnings; use Lxc::object; my %options = (); sub do { my $self = shift; $options{'contname'} = shift or die "Name the container please!\n\n"; eval { $self->{'lxc'}->freeze($options{'contname'}); } or do { print "$@"; die "Cannot freeze $options{'contname'}!\n\n"; }; print "Successfuly frozen!\n"; return; } sub new { my $class = shift; my $self = {}; bless $self, $class; $self->{'lxc'} = new Lxc::object; return $self; } 1; __END__ =head1 AUTHOR Anatoly Burtsev, Eanatolyburtsev@yandex.ruE Pavel Potapenkov, Eppotapenkov@gmail.comE Vladimir Smirnov, Ecivil.over@gmail.comE =head1 COPYRIGHT AND LICENSE Copyright (C) 2011 by Anatoly Burtsev, Pavel Potapenkov, Vladimir Smirnov This library is free software; you can redistribute it and/or modify it under the same terms of GPL v2 or later, or, at your opinion under terms of artistic license. =cut lxctl-0.3.1+debian/Lxctl/vz2lxc.pm0000644000175000017500000001045311652614527017207 0ustar sysadminsysadminpackage Lxctl::vz2lxc; use strict; use warnings; use Getopt::Long; use Lxc::object; use LxctlHelpers::config; my %options = (); my $yaml_conf_dir; my $lxc_conf_dir; my $root_mount_path; my $templates_path; my $vg; my $rsync_opts; my $config; sub migrate_get_opt { my $self = shift; GetOptions(\%options, 'rootsz=s', 'cpus=s', 'cpu-shares=s', 'mem=s', 'io=s', 'fromhost=s', 'remuser=s', 'remport=s', 'remname=s', '--continue!', 'afterstart!'); $options{'remuser'} ||= 'root'; $options{'remport'} ||= '22'; $options{'afterstart'} ||= 0; $options{'rootsz'} ||= (`echo -n \$(ssh $options{'remuser'}\@$options{'fromhost'} "egrep DISKSPACE /etc/vz/conf/$options{'remname'}.conf | cut -d= -f2 | cut -d: -f1 | cut -d'\\"' -f2")`) . "K"; defined($options{'remname'}) or die "You should specify the name of the VZ container!\n\n"; defined($options{'fromhost'}) or die "To which host shold I migrate?\n\n"; } sub re_rsync { my $self = shift; print "Stopping VZ container $options{'remname'}...\n"; die "Failed to stop VZ container $options{'remname'}!\n\n" if system("ssh $options{'remuser'}\@$options{'fromhost'} vzctl stop $options{'remname'} 1>/dev/null"); print "Mounting VZ container $options{'remname'}...\n"; die "Failed to mount VZ container $options{'remname'}!\n\n" if system("ssh $options{'remuser'}\@$options{'fromhost'} vzctl mount $options{'remname'} 1>/dev/null"); print "Re-rsyncing container $options{'contname'}...\n"; die "Failed to re-rsync root filesystem!\n\n" if system("rsync $rsync_opts -e ssh $options{'remuser'}\@$options{'fromhost'}:/var/lib/vz/root/$options{'remname'}/ $root_mount_path/$options{'contname'}/rootfs/ 1>/dev/null"); print "Unmounting VZ container $options{'remname'}...\n"; die "Failed to unmount VZ container $options{'remname'}!\n\n" if system("ssh $options{'remuser'}\@$options{'fromhost'} vzctl umount $options{'remname'} 1>/dev/null"); } sub vz_migrate { my $self = shift; $rsync_opts = $config->get_option_from_main('rsync', 'RSYNC_OPTS'); $rsync_opts ||= "-aH --delete --numeric-ids --exclude 'proc/*' --exclude 'sys/*'"; die "Failed to create container!\n\n" if system("lxctl create $options{'contname'} --empty --rootsz $options{'rootsz'} --save"); print "Rsync'ing VZ container...\n"; print "There were some errors during rsyncing root filesystem. It's definetely NOT okay if it was the only rsync pass.\n\n" if system("rsync $rsync_opts -e ssh $options{'remuser'}\@$options{'fromhost'}:/var/lib/vz/root/$options{'remname'}/ $root_mount_path/$options{'contname'}/rootfs/ 1>/dev/null"); $self->re_rsync(); if (-e "$root_mount_path/$options{'contname'}/rootfs/etc/init/openvz.conf") { print "Found upstart openvz.conf. Modifying...\n"; system("sed -i.bak 's/^.*devpts.*\$//' $root_mount_path/$options{'contname'}/rootfs/etc/init/openvz.conf"); } if ($options{'afterstart'} != 0) { die "Failed to start container $options{'contname'}!\n\n" if system("lxctl start $options{'contname'}"); } } sub migrate_configuration { my $self = shift; die "Failed to migrate MTU!\n\n" if system("lxctl set $options{'contname'} --mtu \$(ssh $options{'remuser'}\@$options{'fromhost'} \"sed -n 's/^[\\t ]\\+mtu[\\t ]\\+\\([0-9]\\+\\)/\\1/p' /var/lib/vz/private/$options{'remname'}/etc/network/interfaces | awk '{print \$2}'\")"); } sub do { my $self = shift; $options{'contname'} = shift or die "Name the container please!\n\n"; $self->migrate_get_opt(); $self->vz_migrate(); $self->migrate_configuration(); } sub new { my $class = shift; my $self = {}; bless $self, $class; $self->{lxc} = new Lxc::object; $root_mount_path = $self->{'lxc'}->get_roots_path(); $templates_path = $self->{'lxc'}->get_template_path(); $yaml_conf_dir = $self->{'lxc'}->get_config_path(); $lxc_conf_dir = $self->{'lxc'}->get_lxc_conf_dir(); $vg = $self->{'lxc'}->get_vg(); $config = new LxctlHelpers::config; return $self; } 1; __END__ =head1 AUTHOR Anatoly Burtsev, Eanatolyburtsev@yandex.ruE Pavel Potapenkov, Eppotapenkov@gmail.comE Vladimir Smirnov, Ecivil.over@gmail.comE =head1 COPYRIGHT AND LICENSE Copyright (C) 2011 by Anatoly Burtsev, Pavel Potapenkov, Vladimir Smirnov This library is free software; you can redistribute it and/or modify it under the same terms of GPL v2 or later, or, at your opinion under terms of artistic license. =cut lxctl-0.3.1+debian/Lxctl/restart.pm0000644000175000017500000000263411652614527017445 0ustar sysadminsysadminpackage Lxctl::restart; use strict; use warnings; use Lxc::object; use Lxctl::start; use Lxctl::stop; my %options = (); my $lxc_conf_dir; use constant { TIMEOUT => 30, }; sub do { my $self = shift; $options{'contname'} = shift or die "Name the container please!\n\n"; my $stop = new Lxctl::stop; my $start = new Lxctl::start; eval { my $status = $self->{'lxc'}->status($options{'contname'}); my $cnt = 0; while ($status eq "RUNNING" && $cnt < TIMEOUT) { $cnt++; $stop->do($options{'contname'}); sleep(1); $status = $self->{'lxc'}->status($options{'contname'}); } die "Cannot stop $options{'contname'}.\n\n" if ($cnt == TIMEOUT); $start->do($options{'contname'}); 1; } or do { print "$@"; die "Cannot restart $options{'contname'}.\n\n"; }; return; } sub new { my $class = shift; my $self = {}; bless $self, $class; $self->{'lxc'} = Lxc::object->new; $lxc_conf_dir = $self->{'lxc'}->get_lxc_conf_dir(); return $self; } 1; __END__ =head1 AUTHOR Anatoly Burtsev, Eanatolyburtsev@yandex.ruE Pavel Potapenkov, Eppotapenkov@gmail.comE Vladimir Smirnov, Ecivil.over@gmail.comE =head1 COPYRIGHT AND LICENSE Copyright (C) 2011 by Anatoly Burtsev, Pavel Potapenkov, Vladimir Smirnov This library is free software; you can redistribute it and/or modify it under the same terms of GPL v2 or later, or, at your opinion under terms of artistic license. =cut lxctl-0.3.1+debian/Lxctl/migrate.pm0000644000175000017500000000744111652614527017412 0ustar sysadminsysadminpackage Lxctl::migrate; use strict; use warnings; use Getopt::Long; use Lxc::object; use LxctlHelpers::config; use LxctlHelpers::SSH; my %options = (); my $yaml_conf_dir; my $lxc_conf_dir; my $root_mount_path; my $templates_path; my $vg; my $ssh; my $rsync_opts; my $config; sub migrate_get_opt { my $self = shift; GetOptions(\%options, 'rootsz=s', 'tohost=s', 'remuser=s', 'remport=s', 'remname=s', 'afterstart!'); $options{'remuser'} ||= 'root'; $options{'remport'} ||= '22'; $options{'remname'} ||= $options{'contname'}; $options{'afterstart'} ||= 1; defined($options{'tohost'}) or die "To which host should I migrate?\n\n"; $ssh = LxctlHelpers::SSH->connect($options{'tohost'}, $options{'remuser'}, $options{'remport'}); } sub re_rsync { my $self = shift; my $first_pass = shift; my $status; eval { $status = $self->{'lxc'}->status($options{'contname'}); } or do { die "Failed to get status for container $options{'contname'}!\n\n"; }; return if ($status ne 'RUNNING' && $first_pass); die "Aborting due to rsinc error.\n\n" if !$first_pass; eval { print "Stopping container $options{'contname'}...\n"; $self->{'lxc'}->stop($options{'contname'}); } or do { die "Failed to stop container $options{'contname'}.\n\n"; }; print "Start second rsync pass...\n"; $self->sync_data() or die "Failed to finish second rsinc pass.\n\n"; } sub copy_config { my $self = shift; print "Sending config to $options{'tohost'}...\n"; $ssh->put_file("$yaml_conf_dir/$options{'contname'}.yaml", "/tmp/$options{'contname'}.yaml"); } sub sync_data { my $self = shift; $rsync_opts = $config->get_option_from_main('rsync', 'RSYNC_OPTS'); my $rsync_from = "$root_mount_path/$options{'contname'}/"; my $rsync_to = "$options{'remuser'}\@$options{'tohost'}:$root_mount_path/$options{'remname'}/"; my $ret = !system("rsync $rsync_opts $rsync_from $rsync_to") or print "There were some problems during syncing root filesystem. It's ok if this is the first pass.\n\n"; return $ret; } sub remote_deploy { my $self = shift; $self->copy_config(); print "Creating remote container...\n"; $ssh->execute("lxctl create $options{'remname'} --empty --load /tmp/$options{'contname'}.yaml") or die "Failed to create remote container.\n\n"; print "Start first rsync pass...\n"; my $first_pass = $self->sync_data(); $self->re_rsync($first_pass); if ($options{'afterstart'} != 0) { print "Starting remote container...\n"; $ssh->execute("lxctl start $options{'remname'}") or die "Failed to start remote container!\n\n"; } } sub do { my $self = shift; $options{'contname'} = shift or die "Name the container please!\n\n"; $self->migrate_get_opt(); $self->remote_deploy(); system("lxctl set $options{'contname'} --autostart 0"); } sub new { my $class = shift; my $self = {}; bless $self, $class; $self->{lxc} = new Lxc::object; $root_mount_path = $self->{'lxc'}->get_roots_path(); $templates_path = $self->{'lxc'}->get_template_path(); $yaml_conf_dir = $self->{'lxc'}->get_config_path(); $lxc_conf_dir = $self->{'lxc'}->get_lxc_conf_dir(); $config = new LxctlHelpers::config; return $self; } 1; __END__ =head1 AUTHOR Anatoly Burtsev, Eanatolyburtsev@yandex.ruE Pavel Potapenkov, Eppotapenkov@gmail.comE Vladimir Smirnov, Ecivil.over@gmail.comE =head1 COPYRIGHT AND LICENSE Copyright (C) 2011 by Anatoly Burtsev, Pavel Potapenkov, Vladimir Smirnov This library is free software; you can redistribute it and/or modify it under the same terms of GPL v2 or later, or, at your opinion under terms of artistic license. =cut lxctl-0.3.1+debian/Lxctl/destroy.pm0000644000175000017500000000562411652614527017454 0ustar sysadminsysadminpackage Lxctl::destroy; use strict; use warnings; use autodie qw(:all); use Getopt::Long; use Lxc::object; use LxctlHelpers::helper; use Lxctl::set; use LxctlHelpers::config; use File::Path; my %options = (); my $yaml_conf_dir; my $lxc_conf_dir; my $root_mount_path; my $templates_path; my $vg; sub do { my $self = shift; $options{'contname'} = shift or die "Name the container please!\n\n"; if ($self->{'lxc'}->status($options{'contname'}) ne 'STOPPED') { die "Container $options{'contname'} is running!\n\n"; } GetOptions(\%options, 'force', 'debug', 'configs'); $self->{'helper'}->fool_proof() if (!$options{force}); my $old_conf_ref = $self->{'config'}->load_file("$yaml_conf_dir/$options{'contname'}.yaml"); my %old_conf = %$old_conf_ref; $old_conf{'roottype'} ||= 'lvm'; my $mounted_path = "/dev/$vg/$options{'contname'}"; if (lc($old_conf{'roottype'}) eq 'file') { $mounted_path = "$root_mount_path/$options{'contname'}.raw"; } if (defined($options{'debug'})) { foreach my $key (sort keys %old_conf) { print "$key = $old_conf{$key}\n"; } } $options{'autostart'} = 0; my $setter = Lxctl::set->new(%options); $setter->set_autostart(); eval { system("umount $mounted_path"); } or do { print "Nothing to umount...\n"; }; if (lc($old_conf{'roottype'}) eq 'file') { rmtree("$root_mount_path/$options{'contname'}.raw"); } elsif (lc($old_conf{'roottype'}) eq 'lvm') { # HIGHLY EXPERIMENTAL. Seems to be source of some bugs. # my $dm_vg = $vg; # $dm_vg =~ s/-/--/g; # system("dmsetup remove -c $dm_vg-$options{'contname'}"); system("lvremove -f /dev/$vg/$options{'contname'}"); } rmtree("$root_mount_path/$options{'contname'}"); rmtree("$lxc_conf_dir/$options{'contname'}"); if (defined($options{'configs'})) { rmtree("$yaml_conf_dir/$options{'contname'}.yaml"); } open(my $fstab_file, '<', "/etc/fstab"); my @fstab = <$fstab_file>; close $fstab_file; open($fstab_file, '>', "/etc/fstab"); for my $line (@fstab) { $line = "" if $line =~ m#^$mounted_path#xs; print $fstab_file $line; } close $fstab_file; return; } sub new { my $class = shift; my $self = {}; bless $self, $class; $self->{'lxc'} = Lxc::object->new; $self->{'helper'} = new LxctlHelpers::helper; $self->{'config'} = new LxctlHelpers::config; $root_mount_path = $self->{'lxc'}->get_roots_path(); $yaml_conf_dir = $self->{'lxc'}->get_config_path(); $lxc_conf_dir = $self->{'lxc'}->get_lxc_conf_dir(); $vg = $self->{'lxc'}->get_vg(); return $self; } 1; __END__ =head1 AUTHOR Anatoly Burtsev, Eanatolyburtsev@yandex.ruE Pavel Potapenkov, Eppotapenkov@gmail.comE Vladimir Smirnov, Ecivil.over@gmail.comE =head1 COPYRIGHT AND LICENSE Copyright (C) 2011 by Anatoly Burtsev, Pavel Potapenkov, Vladimir Smirnov This library is free software; you can redistribute it and/or modify it under the same terms of GPL v2 or later, or, at your opinion under terms of artistic license. =cut lxctl-0.3.1+debian/lxctl_debug0000755000175000017500000000013011652614527016544 0ustar sysadminsysadmin#!/usr/bin/perl use diagnostics -verbose; system("perl -Mdiagnostics ./lxctl @ARGV"); lxctl-0.3.1+debian/Plan0000644000175000017500000000177711652614527015161 0ustar sysadminsysadminRelease planing: Version: 0.1. Date: 29.06.2011 * Basic functionality: create, set, list, start, stop, destroy * LVM creation/deletion * Lxc::control - module for managing lxc tools from perl. Basic functionality * Cgroups API in Lxc::control * Get and set cgroup parameters with lxctl * FS growing * Control LXC's config (set and get info from it) * Simple manual on lxctl Version: 0.2. Date: 03.07.2011 * Plugin system * Some configuration changes * Full manual to lxctl and Lxc::control. * Build-system. Packets for Debian. * Ability to turn lvm off (for quota) * Basic configuration of lxctl from files. Version 0.3. Date: 10.07.2011 * lxct config * Containers's parameters should be saved to config. * Ability to create container from config. * Parallel create (TBD: does somebody needs it?) Version 0.4. Date: 17.07.2011 * Offline-migration * any rootfs.tar to container (For most popular OS's: Fedora 14+, Centos 5.5+, Ubuntu 10.04+, Debian 5.0+) * Stabilize API of Lxc::control * Stabilize API of Plugin system lxctl-0.3.1+debian/bash_completion.d/0000755000175000017500000000000011652614527017720 5ustar sysadminsysadminlxctl-0.3.1+debian/bash_completion.d/lxctl_freeze0000644000175000017500000000014211652614527022326 0ustar sysadminsysadminLXCTL_PLUGINS="$LXCTL_PLUGINS freeze" LXCTL_PLUGIN_freeze="" LXCTL_PLUGIN_USE_VMNAME_freeze="yes" lxctl-0.3.1+debian/bash_completion.d/lxctl0000644000175000017500000000304511652614527020773 0ustar sysadminsysadmin# bash completion for lxctl #have lxctl && _lxctl() { local cur command COMPREPLY=() cur=`_get_cword` command=${COMP_WORDS[1]} BUILTIN="--man --help --version" case $COMP_CWORD in 1) COMMANDS=`echo $BUILTIN $LXCTL_PLUGINS` COMPREPLY=( $( compgen -W "$COMMANDS" -- "$cur" ) ) ;; 2) case $command in '--help'|'--man'|'--version') ;; *) use_vmname="\${LXCTL_PLUGIN_USE_VMNAME_$command}" use_vmname=`eval echo "$use_vmname"` if [ $use_vmname == "yes" ]; then LIST=$(lxctl list --raw 2>/dev/null) COMPREPLY=( $( compgen -W "$LIST" -- "$cur" ) ) else params="\${LXCTL_PLUGIN_$command}" params=`eval echo "$params"` if [ -n "$params" ] ; then COMPREPLY=( $( compgen -W "$params" -- "$cur" ) ) fi fi esac ;; *) case $command in *) params="\${LXCTL_PLUGIN_$command}" params=`eval echo "$params"` if [ -n "$params" ] ; then COMPREPLY=( $( compgen -W "$params" -- "$cur" ) ) fi ;; esac esac } && complete -F _lxctl $default lxctl # Local variables: # mode: shell-script # sh-basic-offset: 4 # sh-indent-comment: t # indent-tabs-mode: nil # End: # ex: ts=4 sw=4 et filetype=sh lxctl-0.3.1+debian/bash_completion.d/lxctl_vz2lxc0000644000175000017500000000027711652614527022307 0ustar sysadminsysadminLXCTL_PLUGINS="$LXCTL_PLUGINS vz2lxc" LXCTL_PLUGIN_vz2lxc="--rootsz --cpus --cpu-shares --mem --io --fromhost --remuser --remport --remname --afterstart" LXCTL_PLUGIN_USE_VMNAME_vz2lxc="yes" lxctl-0.3.1+debian/bash_completion.d/lxctl_set0000644000175000017500000000037411652614527021650 0ustar sysadminsysadminLXCTL_PLUGINS="$LXCTL_PLUGINS set" LXCTL_PLUGIN_set="--ipaddr --hostname --userpasswd --nameserver --searchdomain --rootsz --netmask --defgw --dns --cpus --cpu-shares --mem --io --macaddr --autostart --timezone --mtu" LXCTL_PLUGIN_USE_VMNAME_set="yes" lxctl-0.3.1+debian/bash_completion.d/lxctl_stop0000644000175000017500000000013411652614527022034 0ustar sysadminsysadminLXCTL_PLUGINS="$LXCTL_PLUGINS stop" LXCTL_PLUGIN_stop="" LXCTL_PLUGIN_USE_VMNAME_stop="yes" lxctl-0.3.1+debian/bash_completion.d/lxctl_pid0000644000175000017500000000013611652614527021625 0ustar sysadminsysadminLXCTL_PLUGINS="$LXCTL_PLUGINS pid" LXCTL_PLUGIN_freeze="" LXCTL_PLUGIN_USE_VMNAME_freeze="no" lxctl-0.3.1+debian/bash_completion.d/lxctl_list0000644000175000017500000000017311652614527022025 0ustar sysadminsysadminLXCTL_PLUGINS="$LXCTL_PLUGINS list" LXCTL_PLUGIN_list="--columns --raw --all --noheader" LXCTL_PLUGIN_USE_VMNAME_list="no" lxctl-0.3.1+debian/bash_completion.d/lxctl_migrate0000644000175000017500000000024111652614527022476 0ustar sysadminsysadminLXCTL_PLUGINS="$LXCTL_PLUGINS migrate" LXCTL_PLUGIN_migrate="--rootsz --tohost --remuser --remport --remname --afterstart" LXCTL_PLUGIN_USE_VMNAME_migrate="yes" lxctl-0.3.1+debian/bash_completion.d/lxctl_enter0000644000175000017500000000013711652614527022167 0ustar sysadminsysadminLXCTL_PLUGINS="$LXCTL_PLUGINS enter" LXCTL_PLUGIN_enter="" LXCTL_PLUGIN_USE_VMNAME_enter="yes" lxctl-0.3.1+debian/bash_completion.d/lxctl_restart0000644000175000017500000000014511652614527022535 0ustar sysadminsysadminLXCTL_PLUGINS="$LXCTL_PLUGINS restart" LXCTL_PLUGIN_restart="" LXCTL_PLUGIN_USE_VMNAME_restart="yes" lxctl-0.3.1+debian/bash_completion.d/lxctl_destroy0000644000175000017500000000014511652614527022542 0ustar sysadminsysadminLXCTL_PLUGINS="$LXCTL_PLUGINS destroy" LXCTL_PLUGIN_destroy="" LXCTL_PLUGIN_USE_VMNAME_destroy="yes" lxctl-0.3.1+debian/bash_completion.d/lxctl_mount0000644000175000017500000000021611652614527022212 0ustar sysadminsysadminLXCTL_PLUGINS="$LXCTL_PLUGINS mount" LXCTL_PLUGIN_mount="--add --del --list --from --to --opts --fs --id" LXCTL_PLUGIN_USE_VMNAME_mount="yes" lxctl-0.3.1+debian/bash_completion.d/lxctl_create0000644000175000017500000000050311652614527022312 0ustar sysadminsysadminLXCTL_PLUGINS="$LXCTL_PLUGINS create" LXCTL_PLUGIN_create="--ipaddr --hostname --ostemplate --config --roottype --root --rootsz --netmask --defgw --dns --macaddr --autostart --empty --load --debug --searchdomain --tz --fs --mkfsopts --mountoptions --mtu --userpasswd --pkgopt --addpkg" LXCTL_PLUGIN_USE_VMNAME_create="yes" lxctl-0.3.1+debian/bash_completion.d/lxctl_start0000644000175000017500000000013711652614527022207 0ustar sysadminsysadminLXCTL_PLUGINS="$LXCTL_PLUGINS start" LXCTL_PLUGIN_start="" LXCTL_PLUGIN_USE_VMNAME_start="yes" lxctl-0.3.1+debian/bash_completion.d/lxctl_unfreeze0000644000175000017500000000015011652614527022670 0ustar sysadminsysadminLXCTL_PLUGINS="$LXCTL_PLUGINS unfreeze" LXCTL_PLUGIN_unfreeze="" LXCTL_PLUGIN_USE_VMNAME_unfreeze="yes" lxctl-0.3.1+debian/test.pl0000755000175000017500000000177411652614527015660 0ustar sysadminsysadmin#!/usr/bin/perl use strict; use warnings; use Lxc; use Test::More tests => 10; use Test::Trap; our @list; # Checking container #checking path ok(defined(Lxc::get_config_path()), "LXC_CONF_DIR is defined"); Lxc::set_config_path("/var/lib/"); ok(Lxc::get_config_path() eq "/var/lib/", "LXC_CONF_DIR can be changed"); Lxc::set_config_path("/var/lib/lxc"); @list = Lxc::ls(); ok(Lxc::status() eq "", "Testing Lxc::control::state without vmname"); my @r = trap { Lxc::Kill("abrakadabra", "SIGKILL") }; is ($trap->exit, undef, 'Expecting exit with undef'); is ($trap->stdout, '', "No STDOUT" ); is ($trap->stderr, '', "No STDERR"); print "------------------\n"; ok(Lxc::start("abrakadabra") == 1, "Lxc::start shouldn't start non-existing container"); ok(Lxc::stop("abrakadabra") == 1, "Lxc::stop can stop non-existing machine: just should do nothing"); ok(Lxc::freeze("abrakadabra") == 1, "Lxc::freeze should fail to freeze machine"); ok(Lxc::unfreeze("abrakadabra") == 1, "Lxc::freeze should fail to freeze machine"); lxctl-0.3.1+debian/LICENSE.GPLv20000644000175000017500000004325511652614527016237 0ustar sysadminsysadmin GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Lesser General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. lxctl-0.3.1+debian/Lxc/0000755000175000017500000000000011652614527015056 5ustar sysadminsysadminlxctl-0.3.1+debian/Lxc/object.pm0000644000175000017500000004316611652614527016674 0ustar sysadminsysadmin#!/usr/bin/perl package Lxc::object; use IO::Uncompress::Gunzip qw(gunzip); use warnings; use strict; use 5.010001; use POSIX; use Exporter qw(import); use feature 'state'; my %signals = ( 'SIGHUP' => 1, 'SIGINT' => 2, 'SIGQUIT' => 3, 'SIGILL' => 4, 'SIGTRAP' => 5, 'SIGABRT' => 6, 'SIGIOT' => 6, 'SIGBUS' => 7, 'SIGFPE' => 8, 'SIGKILL' => 9, 'SIGUSR1' => 10, 'SIGSEGV' => 11, 'SIGUSR2' => 12, 'SIGPIPE' => 13, 'SIGALRM' => 14, 'SIGTERM' => 15, 'SIGSTKFLT' => 16, 'SIGCHLD' => 17, 'SIGCONT' => 18, 'SIGSTOP' => 19, 'SIGTSTP' => 20, 'SIGTTIN' => 21, 'SIGTTOU' => 22, 'SIGURG' => 23, 'SIGXCPU' => 24, 'SIGXFSZ' => 25, 'SIGVTALRM' => 26, 'SIGPROF' => 27, 'SIGWINCH' => 28, 'SIGIO' => 29, 'SIGPOLL' => 29, 'SIGPWR' => 30, 'SIGSYS' => 31, 'SIGUNUSED' => 31, ); # sets path to container storage # returns 0 if success #For compatibility. Remove later sub set_config_path { my ($self, $confdir) = @_; my $subname = (caller(0))[3]; if (!defined($confdir)) { die "$subname: No parameter is given\n"; } $self->{YAML_CONFIG_PATH} = $confdir; return 1; } sub get_config_path { my ($self) = @_; return $self->{YAML_CONFIG_PATH}; } sub set_roots_path { my ($self, $confdir) = @_; my $subname = (caller(0))[3]; if (!defined($confdir)) { die "$subname: No parameter is given\n"; } $self->{ROOT_MOUNT_PATH} = $confdir; return 1; } sub get_roots_path { my ($self) = @_; return $self->{ROOT_MOUNT_PATH}; } # New setters getters for local configs. sub set_lxc_log_path { my ($self, $logpath) = @_; my $subname = (caller(0))[3]; if (!defined($logpath)) { die "$subname: No parameter is given\n"; } $self->{LXC_LOG_PATH} = $logpath; return 1; } sub get_lxc_log_path { my ($self) = @_; return $self->{LXC_LOG_PATH}; } sub set_lxc_log_level { my ($self, $loglvl) = @_; my $subname = (caller(0))[3]; if (!defined($loglvl)) { die "$subname: No parameter is given\n"; } $self->{LXC_LOG_LEVEL} = $loglvl; return 1; } sub get_lxc_log_level { my ($self) = @_; return $self->{LXC_LOG_LEVEL}; } sub set_yaml_config_path { my ($self, $confdir) = @_; my $subname = (caller(0))[3]; if (!defined($confdir)) { die "$subname: No parameter is given\n"; } $self->{YAML_CONFIG_PATH} = $confdir; return 1; } sub get_yaml_config_path { my ($self) = @_; return $self->{YAML_CONFIG_PATH}; } sub set_root_mount_path { my ($self, $confdir) = @_; my $subname = (caller(0))[3]; if (!defined($confdir)) { die "$subname: No parameter is given\n"; } $self->{ROOT_MOUNT_PATH} = $confdir; return 1; } sub get_root_mount_path { my ($self) = @_; return $self->{ROOT_MOUNT_PATH}; } sub set_template_path { my ($self, $confdir) = @_; my $subname = (caller(0))[3]; if (!defined($confdir)) { die "$subname: No parameter is given\n"; } $self->{TEMPLATE_PATH} = $confdir; return 1; } sub get_template_path { my ($self) = @_; return $self->{TEMPLATE_PATH}; } sub set_lxc_conf_dir { my ($self, $confdir) = @_; my $subname = (caller(0))[3]; if (!defined($confdir)) { die "$subname: No parameter is given\n"; } $self->{LXC_CONF_DIR} = $confdir; return 1; } sub get_lxc_conf_dir { my ($self) = @_; return $self->{LXC_CONF_DIR}; } sub set_cgroup_path { my ($self, $confdir) = @_; my $subname = (caller(0))[3]; if (!defined($confdir)) { die "$subname: No parameter is given\n"; } $self->{CGROUP_PATH} = $confdir; return 1; } sub get_cgroup_path { my ($self) = @_; return $self->{CGROUP_PATH}; } sub set_vg { my ($self, $conf) = @_; my $subname = (caller(0))[3]; if (!defined($conf)) { die "$subname: No parameter is given\n"; } $self->{VG} = $conf; return 1; } sub get_vg { my ($self) = @_; return $self->{VG}; } sub get_conf_check { my ($self) = @_; return $self->{'skip_conf_check'}; } sub set_conf_check { my ($self, $check) = @_; $self->{'skip_conf_check'} = $check; } # Internal function # Check if 2-nd param is set to y or m in file by 1-st param sub is_inconfig { my ($self, $config_ref, $find) = @_; my $subname = (caller(0))[3]; my @config = @$config_ref; foreach my $string (@config) { if ($string =~ m/$find=[ym]/ ) { return 0; } } return 1; } sub signal_to_int { my ($self, $signal) = @_; if ( $signal =~ /^SIG/ ) { return $signals{"$signal"}; } else { my $sig = "SIG" . $signal; return $signals{"$sig"}; } } # Check if kernel supports LXC, or die. sub check { my ($self) = @_; use integer; use Term::ANSIColor; my $errors = 0; my $warns = 0; if ($> != 0) { print color 'bold red'; print "Error: you are not root!\n"; print color 'reset'; $errors++; } if ($self->{'skip_conf_check'}) { return } # 5-dim array with kernel's config options. # 1-st - option name # 2-nd - is it required or optional # 3-5 is kernel version, when this option was removed from config. # if optional CONFIG is missing it'll result in warning my @config_opts = ( ["CONFIG_NAMESPACES", 1, 99, 99, 99], ["CONFIG_UTS_NS", 1, 99, 99, 99], ["CONFIG_IPC_NS", 1, 99, 99, 99], ["CONFIG_PID_NS", 1, 99, 99, 99], ["CONFIG_USER_NS", 0, 99, 99, 99], ["CONFIG_NET_NS", 0, 99, 99, 99], ["DEVPTS_MULTIPLE_INSTANCES", 1, 99, 99, 99], ["CONFIG_CGROUPS", 1, 99, 99, 99], ["CONFIG_CGROUP_NS", 0, 3, 0, 0], ["CONFIG_CGROUP_DEVICE", 0, 99, 99, 99], ["CONFIG_CGROUP_SCHED", 0, 99, 99, 99], ["CONFIG_CGROUP_CPUACCT", 0, 99, 99, 99], ["CONFIG_CGROUP_MEM_RES_CTLR", 0, 99, 99, 99], ["CONFIG_CPUSETS", 0, 99, 99, 99], ["CONFIG_VETH", 0, 99, 99, 99], ["CONFIG_MACVLAN", 0, 99, 99, 99], ["CONFIG_VLAN_8021Q", 0, 99, 99, 99] ); my $kver = `uname -r`; chop($kver); my ($kver_1, $kver_2, $kver_3) = $kver =~ m/(\d+)\.(\d+)\.*(\d*)/; my $kver_big = $kver_3 + $kver_2 * 1000 + $kver_1*1000*1000; my $config_fh; my @config; if ( -e "/boot/config-$kver" ) { open($config_fh, '<', "/boot/config-$kver") or die "Can't read /boot/config-$kver: $@\n"; @config = <$config_fh>; } elsif ( -e "/proc/config.gz" ) { my $config_str; my $status = gunzip("/proc/config.gz", \$config_str); @config = split($/, $config_str); undef($config_str); } else { die "No kernel config found!\n"; } foreach my $option (@config_opts) { my $test_ver_big = @$option[4] + @$option[3]*1000 + @$option[2]*1000*1000; if ($test_ver_big > $kver_big) { if ($self->is_inconfig(\@config, @$option[0]) != 0) { if (@$option[1] == 0) { print color 'bold yellow'; print "Warning: @$option[0] not supported\n"; print color 'reset'; $warns++; } else { print color 'bold red'; print "Error: @$option[0] not supported\n"; print color 'reset'; $errors++; } } } } if ($warns != 0 || $errors != 0) { print "Errors: $errors\nWarnings: $warns\n"; } if ($errors != 0) { die "Too many errors in config file. LXC won't work properly\n"; } } # Get state for VMName givven in first parameter. # if no parameter is given returns empty string. sub status { my ($self, $name) = @_; my $subname = (caller(0))[3]; if (!defined($name)) { print "$subname: No vmname is given\n"; return ""; } my $status = `lxc-info --name $name 2>&1`; my $lxc_upstream_version = `lxc-version`; $lxc_upstream_version =~ s#.*\s+(\d.*)#$1#; my @lxc_version_tokens = split(/\./, $lxc_upstream_version); my $match; if ($lxc_version_tokens[0] eq 0 && ($lxc_version_tokens[1] > 7 || ($lxc_version_tokens[1] eq 7 && $lxc_version_tokens[2] > 4))) { ($match) = $status =~ m/state:\s+([A-Z]+)/; } else { ($match) = $status =~ m/([A-Z]+$)/; } return $match; } # Returns all existing VMs in array sub ls { my ($self) = @_; my @list; my %vms; my $confpath; my $tmp; my $key; my $subname = (caller(0))[3]; opendir (my $vm_dir, $self->{LXC_CONF_DIR}) or die "$subname: $self->{LXC_CONF_DIR}: $!"; @list = grep {! /^\./ && -d "$self->{LXC_CONF_DIR}/$_" } readdir($vm_dir); closedir($vm_dir); # For each found Container we'll define element in hash. # Then add all running vm's if they are already added # And only after that sort keys of hash and return them foreach $key (@list) { $vms{$key} = ''; } # Listing all running vm and defining key in hash for them @list = `netstat -xa`; @list = grep /$self->{LXC_CONF_DIR}/, @list; foreach $key (@list) { ($tmp) = $key =~ m%$self->{LXC_CONF_DIR}/(.*)/command%; $vms{$tmp} = ''; } @list = sort keys %vms; return @list; } # Attachs to running container. Equivalent to vzctl enter # Returns 0 on success and NO_NAME or output on failure. sub attach { my ($self, $name) = @_; my $subname = (caller(0))[3]; if (!defined($name)) { die "$subname: No vmname is given\n"; } my $status = system("lxc-attach --name $name 2>&1"); if ($status eq "0") { return 1; } else { die "$subname: $status\n"; } } # Suspends running container. # Returns 0 on success, NO_NAME or output on failure sub freeze { my ($self, $name) = @_; my $subname = (caller(0))[3]; if (!defined($name)) { die "$subname: No vmname is given\n"; } my $status = `lxc-freeze --name $name 2>&1`; if ($status eq "") { return 1; } else { die "$subname: $status\n"; } } # Resume suspended container. # Returns 0 on success, 1 on failure sub unfreeze { my ($self, $name) = @_; my $subname = (caller(0))[3]; if (!defined($name)) { die "$subname: No vmname is given\n"; } my $status = `lxc-unfreeze --name $name 2>&1`; if ($status eq "") { return 1; } else { die "$subname: $status\n"; } } # Kills 1-st process of container $NAME with signal $SIG # Requires 2 parameters. # Returns NO_NAME, NO_SIG or output if error and 0 on success. # Signal can be either number or name sub Kill { my ($self, $name, $signal) = @_; my $subname = (caller(0))[3]; if (!defined($name)) { die "$subname: No vmname is given\n"; } if (!defined($signal)) { die "$subname: No signal name is given\n"; } if ($signal =~ /\D/) { $signal = $self->signal_to_int($signal); } my $status = `lxc-kill --name $name $signal 2>&1`; if ($status eq "") { return 1; } else { die "$subname: $status\n"; } } # Start container of NAME=$1 # Config file is $2 (optional) # Write all output to $3 (optional) # Return 0 on success, dies on failure sub start #(name, daemon, config_file, log_file, log_priority) { my ($self, $name, $daemon, $file, $log, $log_priority) = @_; my $subname = (caller(0))[3]; if (!defined($name)) { die "$subname: No vmname is given\n"; } my $myarg="--name $name"; if (defined($file)) { if ($file ne "") { $myarg = $myarg . " -f $file"; } } if (defined($daemon)) { if ($daemon == 1) { $myarg = $myarg . " -d"; } } else { $myarg = $myarg . " -d"; } if (defined($log)) { if ($log ne "") { $myarg = $myarg . " -o $log"; } } if (!defined($log_priority)) { $log_priority = "ERROR"; } $myarg = $myarg . " -l $log_priority"; my $status = `lxc-start $myarg 2>&1`; chop($status); if ($status eq "") { return 1; } else { die "$subname: $status\n"; } } # Stop container with name $1 # Returns 0 on success sub stop { my ($self, $name, $log) = @_; my $subname = (caller(0))[3]; my $myarg = ""; if (!defined($name)) { die "$subname: No vmname is given\n"; } if (defined($log)) { if ($log ne "") { $myarg = $myarg . " -o $log"; } } my $status = `lxc-stop --name $name $myarg 2>&1`; if ($status eq "") { return 1; } else { die "$subname: $status\n"; } } # Get parameter from VM's config # 1-st param is vmname # 2-nd param is what config field to get sub get_conf { my ($self, $name, $param) = @_; my $subname = (caller(0))[3]; if (!defined($name)) { die "$subname: No vmname is given\n\n"; } if (!defined($param)) { die "$subname: No config param defined\n\n"; } open my $config_file, '<', "$self->{LXC_CONF_DIR}/$name/config" or die "$subname: Cannot open config $self->{LXC_CONF_DIR}/$name/config"; my @config = <$config_file>; my @conf = grep { /$param/ } @config; if (defined($conf[0])) { $conf[0] =~ s/$param[ ]+=[ ]+//g; $conf[0] =~ s/\/\//\//; chop($conf[0]); close $config_file; return $conf[0]; } else { close $config_file; die "$subname: Config option not found"; } } sub set_conf { my ($self, $name, $conf, $value) = @_; my $subname = (caller(0))[3]; if (!defined($name)) { die "$subname: No vmname is given\n"; } if (!defined($conf)) { die "$subname: No option name\n"; } if (!defined($value)) { die "$subname: No config value\n"; } open(my $conf_file, '<', "$self->{LXC_CONF_DIR}/$name/config") or die " Failed to open $self->{LXC_CONF_DIR}/$name/config for reading\n"; my @conf = <$conf_file>; close $conf_file; my $search_exists = 0; open($conf_file, '>', "$self->{LXC_CONF_DIR}/$name/config") or die " Failed to open $self->{LXC_CONF_DIR}/$name/config for writing\n"; for my $line (@conf) { $search_exists = 1 if $line =~ s/($conf\s*=\s*).*$/$1 $value/g; print $conf_file $line; } print $conf_file "\n$conf = $value\n" if $search_exists == 0; close $conf_file; my ($from, $to) = @_; return 1; } sub get_cgroup #(vmname, group) { my ($self, $name, $group) = @_; my $subname = (caller(0))[3]; my $result; if (!defined($name)) { die "$subname: No vmname is given\n"; } if (!defined($group)) { die "$subname: No cgroup is given\n"; } if ( -f "$self->{CGROUP_PATH}/$name/$group" ) { open my $cgroup_file, "<", "$self->{CGROUP_PATH}/$name/$group" or die "Can't open file"; $result = <$cgroup_file>; close ($cgroup_file); } else { # TODO: Check if cgroup is mounted and warn if not. $result = $self->get_conf($name, "lxc.cgroup." . $group); ($result) = $result =~ m/((\d|,|-)+$)/; } return $result; } sub set_cgroup{ my ($self, $name, $group, $value, $force) = @_; my $subname = (caller(0))[3]; if (!defined($name)) { die "$subname: No vmname is given\n"; } if (!defined($group)) { die "$subname: No cgroup is given\n"; } if (!defined($value)) { die "$subname: No value is given\n"; } if (!defined($force)) { $force = 0; } if ( -f "$self->{CGROUP_PATH}/$name/$group" ) { open my $cgroup_file, ">", "/cgroup/$name/$group" or die "Failed to open /cgroup/$name/$group"; print $cgroup_file "$value"; close ($cgroup_file); if ($force) { my $group_tmp = "lxc.cgroup." . $group; $self->set_conf($name, $group_tmp, $value); } } else { # TODO: Check if cgroup is mounted and warn if not. if ($force) { my $group_tmp = "lxc.cgroup." . $group; $self->set_conf($name, $group_tmp, $value); } else { die "$subname: $self->{CGROUP_PATH}/$name/$group doesn't exists, aborted...\n\n"; } } return 1; } sub convert_size #(from, to, postfixed) { my ($self, $from, $to, $postfixed) = @_; my $subname = (caller(0))[3]; if (!defined($postfixed)) { $postfixed = 1; } my %convert = ( 'b' => 0, '' => 0, 'k' => 1, 'kib' => 1, 'kb' => 1, 'm' => 2, 'mib' => 2, 'mb' => 2, 'g' => 3, 'gib' => 3, 'gb' => 3, 't' => 4, 'tib' => 4, 'tb' => 4, 'p' => 5, 'pib' => 5, 'pb' => 5, 'e' => 6, 'eib' => 6, 'eb' => 6, ); if (!defined($from)) { die "$subname: Nothing to convert\n"; } if (!defined($to)) { die "$subname: My master, I'm kindly sorry, but I don't know what units do you want me to convert this value to.\n"; } $to = lc $to; $from = lc $from; if (! exists($convert{$to})) { die "$subname: My master, I'm kindly sorry, but I don't know what units do you want me to convert this value to.\n"; } my ($value, $postfix) = $from =~ m/(\d+[.]?\d*)([a-z]*)/ms; if (!defined($postfix)) { $postfix = "b"; } if (!defined($value)) { die "$subname: Non-numeric value! Aborting...\n"; } my $tmp = POSIX::pow(1024, $convert{$postfix}-$convert{$to}) * $value; if ($to ne 'b' && $postfixed == 1) { $tmp = $tmp . $to; } return $tmp; } sub new { my ($class, $skip_conf_check) = @_; state $instance; if (! defined $instance) { $instance = bless {}, $class; $instance->init(); if (defined($skip_conf_check)) { $instance->{'skip_conf_check'} = $skip_conf_check; } else { $instance->{'skip_conf_check'} = 0; } } return $instance; } sub init { my ($self) = @_; $self->{CONFIG_PATH} = "/etc/lxctl"; $self->{ROOTS_PATH} = "/var/lxc/root"; $self->{TEMPLATE_PATH} = "/var/lxc/templates"; $self->{LXC_CONF_DIR} = "/var/lib/lxc"; $self->{CGROUP_PATH} = "/cgroup"; $self->{VG} = "vg00"; } 1; __END__ =head1 NAME Lxc::object =head1 SYNOPSIS Simple OO-wrapper around lxctl. Tested with lxctl 0.7.4.2 =head1 DESCRIPTION Simple OO-wrapper around lxctl. Tested with lxctl 0.7.4.2. =head2 EXPORT None by default. =head2 Exportable constants None by default. =head2 Exportable functions set_config_path($confdir) get_config_path() set_roots_path($confdir) get_roots_path() set_template_path($confdir) get_template_path() set_lxc_conf_dir($confdir) get_lxc_conf_dir() set_cgroup_path($confdir) get_cgroup_path() set_vg($confdir) get_vg() set_conf_check($val) get_conf_check() check() ls() attach($container_name) freeze($container_name) unfreeze($container_name) start($container_name) stop($container_name) Kill($container_name, $signal) get_conf($container_name, $parameter_name) set_conf($container_name, $parameter_name, $value) _deprecated_ get_ip($container_name) # Will be removed in future version get_cgroup($container_name, $parameter) set_cgroup($container_name, $parameter_name, $value, $force): If force is specified, set cgroup param in config even if machine is stoped convert_size($from, $to): Converts from bytes/kib/mib/gib/pib/eib to what was specified. Ex: convert_size("20KB", "MB"); =head1 AUTHOR Anatoly Burtsev, Eanatolyburtsev@yandex.ruE Pavel Potapenkov, Eppotapenkov@gmail.comE Vladimir Smirnov, Ecivil.over@gmail.comE =head1 COPYRIGHT AND LICENSE Copyright (C) 2011 by Anatoly Burtsev, Pavel Potapenkov, Vladimir Smirnov This library is free software; you can redistribute it and/or modify it under the same terms of GPL v2 or later, or, at your opinion under terms of artistic license. =cut lxctl-0.3.1+debian/KnownBugs0000644000175000017500000000130311652614527016165 0ustar sysadminsysadminFix before 0.1: +* Installation should create some folders (/etc/lxc, /var/lxc) * Some systems may have default config not in /var/lib/lxc +* We should depend on at least lxc-0.7.4 * Fix lucid_amd64.tar.gz not to remount / to ro on shutdown * Check config host's network config for br0 (port from autovm's preinst?) Fix somewhere before public release: * Lxc.pm does not always detect errors. (maybe won't fix, becouse when daemonizing container, all output is surpassed, as work-around we can try to eval status and start container not in daemon mode to get normal output from it.) * Distros other then ubuntu may have different default location for lxc's config (e.x. Gentoo). Handle this properly. lxctl-0.3.1+debian/LICENSE.Artistic0000644000175000017500000001171111652614527017117 0ustar sysadminsysadminThe Artistic License Preamble The intent of this document is to state the conditions under which a Package may be copied, such that the Copyright Holder maintains some semblance of artistic control over the development of the package, while giving the users of the package the right to use and distribute the Package in a more-or-less customary fashion, plus the right to make reasonable modifications. Definitions: * "Package" refers to the collection of files distributed by the Copyright Holder, and derivatives of that collection of files created through textual modification. * "Standard Version" refers to such a Package if it has not been modified, or has been modified in accordance with the wishes of the Copyright Holder. * "Copyright Holder" is whoever is named in the copyright or copyrights for the package. * "You" is you, if you're thinking about copying or distributing this Package. * "Reasonable copying fee" is whatever you can justify on the basis of media cost, duplication charges, time of people involved, and so on. (You will not be required to justify it to the Copyright Holder, but only to the computing community at large as a market that must bear the fee.) * "Freely Available" means that no fee is charged for the item itself, though there may be fees involved in handling the item. It also means that recipients of the item may redistribute it under the same conditions they received it. 1. You may make and give away verbatim copies of the source form of the Standard Version of this Package without restriction, provided that you duplicate all of the original copyright notices and associated disclaimers. 2. You may apply bug fixes, portability fixes and other modifications derived from the Public Domain or from the Copyright Holder. A Package modified in such a way shall still be considered the Standard Version. 3. You may otherwise modify your copy of this Package in any way, provided that you insert a prominent notice in each changed file stating how and when you changed that file, and provided that you do at least ONE of the following: a) place your modifications in the Public Domain or otherwise make them Freely Available, such as by posting said modifications to Usenet or an equivalent medium, or placing the modifications on a major archive site such as ftp.uu.net, or by allowing the Copyright Holder to include your modifications in the Standard Version of the Package. b) use the modified Package only within your corporation or organization. c) rename any non-standard executables so the names do not conflict with standard executables, which must also be provided, and provide a separate manual page for each non-standard executable that clearly documents how it differs from the Standard Version. d) make other distribution arrangements with the Copyright Holder. 4. You may distribute the programs of this Package in object code or executable form, provided that you do at least ONE of the following: a) distribute a Standard Version of the executables and library files, together with instructions (in the manual page or equivalent) on where to get the Standard Version. b) accompany the distribution with the machine-readable source of the Package with your modifications. c) accompany any non-standard executables with their corresponding Standard Version executables, giving the non-standard executables non-standard names, and clearly documenting the differences in manual pages (or equivalent), together with instructions on where to get the Standard Version. d) make other distribution arrangements with the Copyright Holder. 5. You may charge a reasonable copying fee for any distribution of this Package. You may charge any fee you choose for support of this Package. You may not charge a fee for this Package itself. However, you may distribute this Package in aggregate with other (possibly commercial) programs as part of a larger (possibly commercial) software distribution provided that you do not advertise this Package as a product of your own. 6. The scripts and library files supplied as input to or produced as output from the programs of this Package do not automatically fall under the copyright of this Package, but belong to whomever generated them, and may be sold commercially, and may be aggregated with this Package. 7. C or perl subroutines supplied by you and linked into this Package shall not be considered part of this Package. 8. The name of the Copyright Holder may not be used to endorse or promote products derived from this software without specific prior written permission. 9. THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. The End