Monday, 7 March 2011

Configure a lofs (LOopback virtual FileSystem) shared area between zones

Operating System: Solaris 10 & variants

Create area to be shared on global zone, e.g. /share

mkdir /share

Configure the zones to use the shared area:

# zonecfg -z zone1
zonecfg:zone1> add fs
zonecfg:zone1> set dir=/share
zonecfg:zone1> set special=/share/sharex
zonecfg:zone1> set type=lofs
zonecfg:zone1> end
zonecfg:zone1> verify
zonecfg:zone1> commit
zonecfg:zone1> exit
# zonecfg -z zone2
zonecfg:zone2> add fs
zonecfg:zone2> set dir=/share
zonecfg:zone2> set special=/share/sharex
zonecfg:zone2> set type=lofs
zonecfg:zone2> end
zonecfg:zone2> verify
zonecfg:zone2> commit
zonecfg:zone2> exit
#

Reboot both zones

# zoneadm -z zone1 reboot
# zoneadm -z zone2 reboot

The filesystems will now be available and fully writeable for both zones.  There is no limit to the number of zones that can share the filesystem.

Thursday, 10 February 2011

Perl: how to inherit and modify the constructor of parent class

This reminder post will be by example, but basically talks about how to inherit a constructor from a parent class, then modify it slightly.  It's also a good case for remembering the reason for this blog - which is to serve as assistance to my memory, and isn't in any way a guidebook or even set of suggestions for how to do things.  This is particularly true of my forays into perl hackery.

Right then.  For this example, we'll start with two simple classes, "Parent" and "Child".  The inheritance relationship works exactly as you'd expect from the names:


Code for inheritance1.pl

#!/usr/bin/perl -w

use strict;

#--------------------------------

package Parent;
sub new {
    my $class=shift;
    my $string1=shift;
    print("1) Called $class constructor with: " . $string1 . "\n");
    my $self={
        string1 => $string1,
        };
    bless($self, $class);
    return $self;
    }

1;

#--------------------------------

package Child;
our @ISA='Parent';
sub new {
    my $class=shift;
    my $string1=shift;
    my $self=Parent->new($string1);
    print("2) Called $class constructor with: " . $string1 . "\n");
    bless($self,$class);
    return $self;
    }

1;

#--------------------------------


my $testObject1=Parent->new("Parent's String");
my $testObject2=Child->new("Child's String");

print("Parent object has stored: ",$testObject1->{string1},"\n");
print("Child object has stored: ",$testObject2->{string1},"\n");

exit 0;


This produces the output:
1) Called Parent constructor with: Parent's String
1) Called Parent constructor with: Child's String
2) Called Child constructor with: Child's String
Parent object has stored: Parent's String
Child object has stored: Child's String




The line of interest here is:
    my $self=Parent->new($string1);

Here the constructor for Child inherits the properties of the constructor for Parent.  As a result, whenever we call the constructor for Child, we see that code from the parent constructor is run.  Additional code from the Child constructor is also run.  However we've really done nothing useful here, as we haven't added any great extra feature to the Child constructor.  So what if we want to store a new parameter into our new object?



Code for inheritance2.pl

#!/usr/bin/perl -w

use strict;

#--------------------------------

package Parent;
sub new {
    my $class=shift;
    my $string1=shift;
    print("1) Called $class constructor with: " . $string1 . "\n");
    my $self={
        string1 => $string1,
        };
    bless($self, $class);
    return $self;
    }

1;

#--------------------------------

package Child;
our @ISA='Parent';
sub new {
    my $class=shift;
    my $string1=shift;
    my $string2=shift;
    my $self=Parent->new($string1);
    print("2) Called $class constructor with: " . $string2 . "\n");
    $self={
        string2 => $string2,
        };
    bless($self,$class);
    return $self;
    }

1;

#--------------------------------


my $testObject1=Parent->new("Parent's String");
my $testObject2=Child->new("Child's 1st String","Child's 2nd String");

print("Parent object has stored: ",$testObject1->{string1},"\n");
print("Child object has stored: ",$testObject2->{string1},"\n");
print("Child object has stored: ",$testObject2->{string2},"\n");

exit 0;


This produces the output:
1) Called Parent constructor with: Parent's String
1) Called Parent constructor with: Child's 1st String
2) Called Child constructor with: Child's 2nd String
Parent object has stored: Parent's String
Use of uninitialized value in print at ./inheritance2.pl line 47.
Child object has stored:
Child object has stored: Child's 2nd String



That didn't work at all!  That shouldn't be too much of a surprise, really, as we've overwritten the declaration of the $self hash.  We could expand it to include everything that the Parent class has, and in this case, it wouldn't take a lot of extra typing, but it's hardly making use of our Object Oriented design, and if there was lots of exciting configuration defined by the $self declaration, we'd lose it all, or have to recreate it.  Better is:



Code for inheritance3.pl
#!/usr/bin/perl -w

use strict;

#--------------------------------

package Parent;
sub new {
    my $class=shift;
    my $string1=shift;
    print("1) Called $class constructor with: " . $string1 . "\n");
    my $self={
        string1 => $string1,
        };
    bless($self, $class);
    return $self;
    }

1;

#--------------------------------

package Child;
our @ISA='Parent';
sub new {
    my $class=shift;
    my $string1=shift;
    my $string2=shift;
    my $self=Parent->new($string1);
    $self->{string2} = $string2;
    print("2) Called $class constructor with: " . $string2 . "\n");
    bless($self,$class);
    return $self;
    }

1;

#--------------------------------


my $testObject1=Parent->new("Parent's String");
my $testObject2=Child->new("Child's 1st String","Child's 2nd String");

print("Parent object has stored: ",$testObject1->{string1},"\n");
print("Child object has stored: ",$testObject2->{string1},"\n");
print("Child object has stored: ",$testObject2->{string2},"\n");

exit 0;


This produces the output:
1) Called Parent constructor with: Parent's String
1) Called Parent constructor with: Child's 1st String
2) Called Child constructor with: Child's 2nd String
Parent object has stored: Parent's String
Child object has stored: Child's 1st String
Child object has stored: Child's 2nd String


And that's just as we expect.

Wednesday, 9 February 2011

CPAN problem with Compress::Zlib

Perl Version:  5.8.4 
Operating System: OpenIndiana oi_148
Error Message(s) seen:
Undefined subroutine &Compress::Zlib::gzopen called at /usr/perl5/5.8.4/lib/CPAN/Tarzip.pm line 122


This error showed up while installing a module called Config::Crontab.  It potentially could have caused problems with installing any module though, as I'm pretty sure that it's down to missing dependencies for Compress::Zlib.

The error presented itself as follows:

 cpan[2]> install Config::Crontab
Running install for module 'Config::Crontab'
Running make for S/SC/SCOTTW/Config-Crontab-1.30.tar.gz
CPAN: LWP::UserAgent loaded ok (v5.835)
Fetching with LWP:
  ftp://mirror.ox.ac.uk/sites/www.cpan.org/authors/id/S/SC/SCOTTW/Config-Crontab-1.30.tar.gz
CPAN: Digest::SHA loaded ok (v5.50)
Fetching with LWP:
  ftp://mirror.ox.ac.uk/sites/www.cpan.org/authors/id/S/SC/SCOTTW/CHECKSUMS
Catching error: "Undefined subroutine &Compress::Zlib::gzopen called at /usr/perl5/5.8.4/lib/CPAN/Tarzip.pm line 122.\cJ" at /usr/perl5/5.8.4/lib/CPAN.pm line 391
    CPAN::shell() called at -e line 1

As this suggests a problem with Compress::Zlib, I tried to update it:
cpan[3]> install Compress::Zlib
Compress::Zlib is up to date (2.033).j

The solution was to remove Compress::Zlib and reinstall it, which then picked up missing dependencies and installed them:

root@ph_opensolaris:/root# ls /usr/perl5/site_perl/5.8.4/Compress/
Zlib.pm
root@ph_opensolaris:/root# rm /usr/perl5/site_perl/5.8.4/Compress/Zlib.pm

cpan[1]> install Compress::Zlib



Problems using CPAN / Perl Modules with OpenSolaris (& OpenIndiana)

Perl version: 5.8.4
Operating System: OpenIndiana oi_148 (I imagine that OpenSolaris also sees these problems)
Error Message(s) seen:  
cc: unrecognized option `-KPIC'
cc: language ildoff not recognized
cc: SHA.c: linker input file unused because linking not done
[...]
cc: SHA.o: No such file or directory
cc: no input files
Make had returned bad status, install seems impossible


The key errors here are the 'unrecognised option' and 'language not recognised' messages, which are coming from cc.  cc on OpenIndiana is a link from /usr/gnu/bin/cc to /usr/sfw/bin/gcc - and gcc is not recognising some of the flags that are passed to it.

 These are specified in this file:

/usr/perl5/5.8.4/lib/i86pc-solaris-64int/Config.pm

by the lines:

cccdlflags='-KPIC'

and,

optimize='-xO3 -xspace -xildoff'

To solve the problem, we need to remove (all) the offending entries, so that these two lines read:

cccdlflags=''

and,

optimize=''

Wednesday, 27 October 2010

Migrating a zone between two global hosts

One feature of Solaris zones is the ability to migrate a non-global zone from one global host to another. In the case of zones built on zfs filesystems, this can be done as follows. (If you're using UFS, substitute tar commands for the zfs commands).

The rationale for this move is that the zone has an interface on VLAN101, but needs a second interface on VLAN102. The global host it is currently attached to (mars) does not have an interface on VLAN102. Another global host (venus) has interfaces on both VLANs, and is therefore an appropriate (in fact the only) candidate for receiving the zone.

Global1=mars
Global2=venus
zone=scorpio

First, let's check the Solaris 10 release level on each box.

(cat /etc/release)
mars: Solaris 10 10/08 s10s_u6wos_07b SPARC
venus: Solaris 10 10/09 s10s_u8wos_08a SPARC

And architecture:
(uname -m)
mars: sun4v
venus: sun4v



Moving from a lower release to a higher one will certainly involve some packages being upgraded, but Solaris will handle most of that automatically. Hopefully.



So, first, let's halt the zone and detach it:
mars# zoneadm -z scorpio halt
mars# zoneadm -z scorpio detach


The zone is now ready for transfer, which we will do with the convenient zfs commands available. If you are not using zfs, tar and ssh will work just as well.


First we'll take a snapshot of the zone filesystem:
mars# zfs snapshot -r rootpool/zones/scorpio@mars

We also have a zfs dataset allocated to the zone, so we'll snapshot that too:
mars# zfs snapshot -r datapool/zones/scorpio@mars

Just quickly check those snapshots have been taken:
mars# zfs list -t snapshot|grep scorpio
rootpool/zones/scorpio@mars
datapool/zones/scorpio@mars
datapool/zones/scorpio/zonedata@mars
datapool/zones/scorpio/zonedata/opt@mars

Great. Now for sending them. Firstly, we'll need to allow a root login (temporarily) on venus:
venus# vi /etc/ssh/sshd_config
change
PermitRootLogin no
to
PermitRootLogin yes
venus# svcadm restart ssh:default

let's send them:
mars# zfs send rootpool/zones/scorpio@mars | ssh root@venus "zfs recv rootpool/zones/scorpio"
Password:
mars# zfs send datapool/zones/scorpio/zonedata@mars | ssh root@venus "zfs recv rootpool/zones/scorpio/zonedata"
Password:
mars# zfs send datapool/zones/scorpio/zonedata/opt@mars | ssh root@venus "zfs recv rootpool/zones/scorpio/zonedata/opt"
Password:

Note that they're being received to the rootpool on venus, as it does not have a datapool configured.

Obviously now change the root login permission back to 'no':
venus# vi /etc/ssh/sshd_config
change
PermitRootLogin yes
to
PermitRootLogin no
venus# svcadm restart ssh:default

Now the datasets are transferred, we can attach the detached zone to the new global host. This is done with zonecfg, as with creating a new zone, but using create -a to specify that we are attaching a zone.
venus# zonecfg -z scorpio
scorpio: No such zone configured
Use 'create' to begin configuring a new zone.
zonecfg:scorpio; create -a /zones/scorpio
Now let's check that has imported all the zone config:
zonecfg:scorpio; info
zonename: scorpio
zonepath: /zones/scorpio
brand: native
autoboot: true
bootargs:
pool:
limitpriv:
scheduling-class: FSS
ip-type: shared
[cpu-shares: 1]
inherit-pkg-dir:
dir: /lib
inherit-pkg-dir:
dir: /platform
inherit-pkg-dir:
dir: /sbin
inherit-pkg-dir:
dir: /usr
net:
address: 10.278.28.35
physical: e1000g101001
defrouter not specified
capped-memory:
physical: 4G
[swap: 4G]
dataset:
name: datapool/zones/scorpio/zonedata
rctl:
name: zone.max-swap
value: (priv=privileged,limit=2147483648,action=deny)
rctl:
name: zone.cpu-shares
value: (priv=privileged,limit=1,action=none)
We'll need to make some changes here, as the zfs dataset is no longer part of datapool, but rootpool, because the interface on VLAN 101 is named differently on venus, and because we need the zone to have a new interface on VLAN 102.

Change interface name:
zonecfg:scorpio; select net address=10.278.28.35
zonecfg:scorpio:net; info
net:
address: 10.278.28.35
physical: e1000g101001
defrouter not specified
zonecfg:scorpio:net; set physical=aggr101001
zonecfg:scorpio:net; end
Add new interface:
zonecfg:scorpio; add net
zonecfg:scorpio:net; set address=10.278.38.35
zonecfg:scorpio:net; set physical=aggr102001
zonecfg:scorpio:net; end
Change dataset name:
zonecfg:scorpio:dataset; set dataset rootpool/zones/scorpio/zonedata/opt@mars
zonecfg:scorpio:dataset; set name=rootpool/zones/scorpio/zonedata/opt
zonecfg:scorpio:dataset; end
Commit these changes and exit:
zonecfg:scorpio; commit
zonecfg:scorpio; exit

The zone is now configured and ready to be attached. But (as we read in the zoneadm manpages):
By default, zoneadm checks package and patch levels on
the machine to which the zone is to be attached. If the
packages/patches that the zone depends on from the glo-
bal zone are different (have different revision numbers)
from the dependent packages/patches on the source
machine, zoneadm reports these conflicts and does not
perform the attach.
Adding a '-u' flag tells Solaris to attempt to upgrade any packages/patches at a lower version in the new zone, though it cannot downgrade packages/patches.

Running without '-u' gives you a long list of package and patch inconsistencies, as we are moving from update 6 to update 8.
root@venus# zoneadm -z scorpio attach -u
zoneadm: zone 'scorpio': ERROR: attempt to downgrade package SUNWlucfg, the source had patch 121430-43 but this system only has 121430-42
zoneadm: zone 'scorpio': ERROR: attempt to downgrade package SUNWlur, the source had patch 121430-43 but this system only has 121430-42
zoneadm: zone 'scorpio': ERROR: attempt to downgrade package SUNWluu, the source had patch 121430-43 but this system only has 121430-42
zoneadm: zone 'scorpio': ERROR: attempt to downgrade package SUNWservicetagr 1.1.4,REV=2008.04.25.09.06 to version 1.0,REV=2007.05.21.20.36
zoneadm: zone 'scorpio': ERROR: attempt to downgrade package SUNWservicetagu 1.1.4,REV=2008.04.25.09.06 to version 1.0,REV=2007.05.21.20.36
zoneadm: zone 'scorpio': ERROR: attempt to downgrade package SUNWstosreg 1.1.4,REV=2008.04.25.09.06 to version 1.0,REV=2007.05.21.20.36
Somewhere along the line, someone has upgraded these packages, and patch 121430 on the u6 box, and they are at a higher level on the zone than they are on the new target global_host.

If this happens to you, it's likely that you'll have a completely different set of patches and packages, and you should proceed accordingly. Here, the Services Tools Bundle has been installed, and the aforementioned patch 121430. I downloaded and applied these to venus.
# zoneadm -z scorpio attach -u
Getting the list of files to remove
Removing 326 files
Remove 24 of 24 packages
Installing 189 files
Add 15 of 15 packages
Updating editable files
The file within the zone contains a log of the zone update.

# zoneadm list -iv | grep scorpio
- scorpio installed /zones/scorpio native shared

# zoneadm -z scorpio boot
# zoneadm list -iv | grep scorpio
81 scorpio running /zones/scorpio native shared
Success!