Sysync

 view release on metacpan or  search on metacpan

lib/Sysync.pm  view on Meta::CPAN

        my $keys = join("\n", @{$user->{ssh_keys} || []});
        $keys .= "\n" if $keys;

        next unless $keys;

        push @ssh_keys, {
            username => $user->{username},
            keys     => $keys,
            uid      => $user->{uid},
        };
    }

    return {
        passwd   => $passwd,
        shadow   => $shadow,
        group    => $group,
        gshadow  => $gshadow,
        ssh_keys => \@ssh_keys,
        data     => $data,
    };
}

=head3 get_host_files

Generate a list of files with their content.

 Returns hashref:
 '/etc/filename.conf' => {
    mode     => 600,
    gid      => 0,
    uid      => 0,
    data     => 'data is here'
 }

=cut

sub get_host_files { die 'needs implemented' }

=head3 update_host_files

Build host files from specifications.

=cut

sub update_host_files
{
    my ($self, $host) = @_;

    my $stagefilesdir = $self->stagefilesdir;
    my $stagedir      = $self->stagedir;

    my $r = 0;

    next unless $self->is_valid_host($host);
    my $files = $self->get_host_files($host);

    unless (-d "$stagefilesdir/$host")
    {
        mkdir "$stagefilesdir/$host";
        chmod 0755, "$stagefilesdir/$host";
        chown 0, 0, "$stagefilesdir/$host";

        $self->log("creating: $stagefilesdir/$host");
        $r++;
    }

    unless (-d "$stagefilesdir/$host/etc")
    {
        mkdir "$stagefilesdir/$host/etc";
        chmod 0755, "$stagefilesdir/$host/etc";
        chown 0, 0, "$stagefilesdir/$host/etc";

        $self->log("creating: $stagefilesdir/$host/etc");
            $r++;
    }

    unless (-d "$stagefilesdir/$host/etc/ssh")
    {
        mkdir "$stagefilesdir/$host/etc/ssh";
        chmod 0755, "$stagefilesdir/$host/etc/ssh";
        chown 0, 0, "$stagefilesdir/$host/etc/ssh";

        $self->log("creating: $stagefilesdir/$host/etc/ssh");
        $r++;
    }

    unless (-d "$stagefilesdir/$host/etc/ssh/authorized_keys")
    {
        mkdir "$stagefilesdir/$host/etc/ssh/authorized_keys";
        chmod 0755, "$stagefilesdir/$host/etc/ssh/authorized_keys";
        chown 0, 0, "$stagefilesdir/$host/etc/ssh/authorized_keys";

        $self->log("creating: $stagefilesdir/$host/etc/ssh/authorized_keys");
        $r++;
    }

    for my $path (sort keys %{ $files || {} })
    {
        my $item = $files->{$path};
        next unless $item->{directory};

        $item->{directory} =~ s/\/$//;

        next if $item->{directory} eq '/etc';
        next if $item->{directory} eq '/etc/ssh';
        next if $item->{directory} eq '/etc/ssh/authorized_keys';

        $item->{directory} =~ s/^\///;

        my @path_parts = split('/', $item->{directory});
        my $filename   = pop @path_parts;
        my $parent_dir = join('/', @path_parts);

        $item->{file} =~ s/^\///;

        unless (-d "$stagefilesdir/$host/$parent_dir")
        {
            die "[$host: error] parent directory $parent_dir not defined for $item->{directory}\n";
        }

        unless (-d "$stagefilesdir/$host/$item->{directory}")
        {
            mkdir "$stagefilesdir/$host/$item->{directory}";
            $self->log("creating: $stagefilesdir/$host/$item->{directory}");
        }

        my $mode = sprintf("%04i", $item->{mode});
        chmod $mode, "$stagefilesdir/$host/$item->{directory}";
        chown $item->{uid}, $item->{gid}, "$stagefilesdir/$host/$item->{directory}";

        $r++;
    }

    for my $path (keys %{ $files || {} })
    {
        my $item = $files->{$path};
        next unless $item->{file};

        my @path_parts = split('/', $item->{file});

        my $filename   = pop @path_parts;
        my $parent_dir = join('/', @path_parts);

        $item->{file} =~ s/^\///;

        unless (-d "$stagefilesdir/$host/$parent_dir")
        {
            die "[$host: error] directory $parent_dir not defined for $item->{file}\n";
        }

        if ($self->write_file_contents("$stagefilesdir/$host/$item->{file}", $item->{data}))
        {
            $r++;
        }

        my $mode = sprintf("%04i", $item->{mode});

        chmod oct($mode), "$stagefilesdir/$host/$item->{file}";
        chown $item->{uid}, $item->{gid}, "$stagefilesdir/$host/$item->{file}";
    }

    # get list of staging directory contents
    my @staged_file_list;
    File::Find::find({
        wanted   => sub { push @staged_file_list, $_ },
        no_chdir => 1,
    }, "$stagefilesdir/$host");

    for my $staged_file (@staged_file_list)
    {
        next unless -e $staged_file;

        (my $local_staged_file = $staged_file) =~ s/^$stagefilesdir\/$host//;
        next unless $local_staged_file;

        next if $local_staged_file eq '/';
        next if $local_staged_file eq '/etc';
        next if $local_staged_file eq '/etc/ssh';
        next if $local_staged_file eq '/etc/ssh/authorized_keys';
        next if $local_staged_file eq '/var/lib/extrausers';

        unless ($files->{$local_staged_file})
        {
            if (-d $staged_file)
            {
                $self->log("deleting directory: $staged_file");
                rmtree($staged_file);
            }
            elsif (-e $staged_file)
            {
                $self->log("deleting file: $staged_file");
                unlink($staged_file);
            }
            $r++;
        }
    }

    return $r;
}

=head3 update_all_hosts

Iterate through every host and build password files.

=cut

sub update_all_hosts
{
    my ($self, %params) = @_;

    # get list of hosts along with image name
    my $hosts = $params{hosts} || $self->get_all_hosts;

    # first, build staging directories
    my @hosts = keys %{ $hosts->{hosts} || {} };

    my $stagedir = $self->stagedir;

    my $r = 0;

    if  ($hosts->{extra_users} || $self->{extra_users})
    {
        $hosts->{passwd_file} = '/var/lib/extrausers/passwd';
        $hosts->{group_file} = '/var/lib/extrausers/group';
        $hosts->{shadow_file} = '/var/lib/extrausers/shadow';
        $hosts->{gshadow_file} = '/var/lib/extrausers/gshadow';
    }

    my $passwd_file = $hosts->{passwd_file} || '/etc/passwd';
    my $group_file  = $hosts->{group_file}  || '/etc/group';
    my $shadow_file = $hosts->{shadow_file} || '/etc/shadow';
    my $gshadow_file = $hosts->{gshadow_file} || '/etc/gshadow';

    for my $host (@hosts)
    {
        next unless $self->is_valid_host($host);

        unless (-d "$stagedir/$host")
        {
            mkdir "$stagedir/$host";
            chmod 0755, "$stagedir/$host";
            chown 0, 0, "$stagedir/$host";
            $self->log("creating: $stagedir/$host");
            $r++;
        }

        unless (-d "$stagedir/$host/etc")
        {
            mkdir "$stagedir/$host/etc";
            chmod 0755, "$stagedir/$host/etc";
            chown 0, 0, "$stagedir/$host/etc";
            $self->log("creating: $stagedir/$host/etc");
            $r++;
        }

        unless (-d "$stagedir/$host/etc/ssh")
        {
            mkdir "$stagedir/$host/etc/ssh";
            chmod 0755, "$stagedir/$host/etc/ssh";
            chown 0, 0, "$stagedir/$host/etc/ssh";
            $self->log("creating: $stagedir/$host/etc/ssh");
            $r++;
        }

        unless (-d "$stagedir/$host/etc/ssh/authorized_keys")
        {
            mkdir "$stagedir/$host/etc/ssh/authorized_keys";
            chmod 0755, "$stagedir/$host/etc/ssh/authorized_keys";
            chown 0, 0, "$stagedir/$host/etc/ssh/authorized_keys";
            $self->log("creating: $stagedir/$host/etc/ssh/authorized_keys");
            $r++;
        }

        unless (-d "$stagedir/$host/var")
        {
            mkdir "$stagedir/$host/var";
            chmod 0755, "$stagedir/$host/var";
            chown 0, 0, "$stagedir/$host/var";

            $self->log("creating: $stagedir/$host/var");
            $r++;
        }

        unless (-d "$stagedir/$host/var/lib")
        {
            mkdir "$stagedir/$host/var/lib";
            chmod 0755, "$stagedir/$host/var/lib";
            chown 0, 0, "$stagedir/$host/var/lib";

            $self->log("creating: $stagedir/$host/var/lib");
            $r++;
        }

        unless (-d "$stagedir/$host/var/lib/extrausers")
        {
            mkdir "$stagedir/$host/var/lib/extrausers";
            chmod 0755, "$stagedir/$host/var/lib/extrausers";
            chown 0, 0, "$stagedir/$host/var/lib/extrausers";

            $self->log("creating: $stagedir/$host/var/lib/extrausers");
            $r++;
        }

        # write host files
        my $ent_data = $self->get_host_ent($host);

        next unless $ent_data;

        for my $key (@{ $ent_data->{ssh_keys} || [] })
        {
            my $username = $key->{username};
            my $uid      = $key->{uid};
            my $text     = $key->{keys};

            if ($self->write_file_contents("$stagedir/$host/etc/ssh/authorized_keys/$username", $text))
            {
                chmod 0600, "$stagedir/$host/etc/ssh/authorized_keys/$username";
                chown $uid, 0, "$stagedir/$host/etc/ssh/authorized_keys/$username";
                $r++;
            }
        }

        my ($shadow_group) =
            grep { $_->{groupname} eq 'shadow' }
                @{ $ent_data->{data}{groups} || [ ] };

        $shadow_group = {} unless defined $shadow_group;
        $shadow_group = $shadow_group->{gid} || 0;

        if ($self->write_file_contents("$stagedir/$host/$passwd_file", $ent_data->{passwd}))
        {
            chmod 0644, "$stagedir/$host/$passwd_file";
            chown 0, 0, "$stagedir/$host/$passwd_file";
            $r++;
        }

        if ($self->write_file_contents("$stagedir/$host/${group_file}", $ent_data->{group}))
        {
            chmod 0644, "$stagedir/$host/${group_file}";
            chown 0, 0, "$stagedir/$host/${group_file}";
            $r++;
        }

        if ($self->write_file_contents("$stagedir/$host/${shadow_file}", $ent_data->{shadow}))
        {
            chmod 0640, "$stagedir/$host/${shadow_file}";
            chown 0, $shadow_group, "$stagedir/$host/${shadow_file}";
            $r++;
        }

       if ($self->write_file_contents("$stagedir/$host/${gshadow_file}", $ent_data->{gshadow}))
        {
            chmod 0640, "$stagedir/$host/${gshadow_file}";
            chown 0, $shadow_group, "$stagedir/$host/${gshadow_file}";
            $r++;
        }
   }

    return $r;
}

=head3 write_file_contents

=cut

sub write_file_contents
{
    my ($self, $file, $data) = @_;

    # check to see if this differs

    if (-e $file)
    {
        if (md5_hex($data) eq md5_hex($self->read_file_contents($file)))
        {
            return;
        }
    }

    $self->log("writing: $file");

    if (-e $file)
    {
        unlink($file) or die $!;
    }

    open(F, "> $file") or die $!;
    print F $data;
    close(F);

    return 1;
}

=head3 read_file_contents

=cut

sub read_file_contents
{
    my ($self, $file, %params) = @_;

    die "error: $file does not exist\n"
        if $params{must_exist} and not -f $file;

    return unless -e $file;

    open(my $fh, $file);
    my @content = <$fh>;
    close($fh);

    return join('', @content);
}

1;



( run in 0.520 second using v1.01-cache-2.11-cpan-5511b514fd6 )