# # (c) Jan Gehring <jan.gehring@gmail.com> # =head1 NAME Rex::Commands::Cloud - Cloud Management Commands =head1 DESCRIPTION With this Module you can manage different Cloud services. Currently it supports Amazon EC2, Jiffybox and OpenStack. Version <= 1.0: All these functions will not be reported. =head1 SYNOPSIS use Rex::Commands::Cloud; cloud_service "Amazon"; cloud_auth "your-access-key", "your-private-access-key"; cloud_region "ec2.eu-west-1.amazonaws.com"; task "list", sub { print Dumper cloud_instance_list; print Dumper cloud_volume_list; }; task "create", sub { my $vol_id = cloud_volume create => { size => 1, zone => "eu-west-1a", }; cloud_instance create => { image_id => "ami-xxxxxxx", name => "test01", key => "my-key", volume => $vol_id, zone => "eu-west-1a", }; }; task "destroy", sub { cloud_volume detach => "vol-xxxxxxx"; cloud_volume delete => "vol-xxxxxxx"; cloud_instance terminate => "i-xxxxxxx"; }; =head1 EXPORTED FUNCTIONS =cut package Rex::Commands::Cloud; use v5.12.5; use warnings; our $VERSION = '1.14.3.1'; # TRIAL VERSION require Rex::Exporter; use base qw(Rex::Exporter); use vars qw(@EXPORT $cloud_service $cloud_region @cloud_auth); use Rex::Logger; use Rex::Config; use Rex::Cloud; use Rex::Group::Entry::Server; @EXPORT = qw(cloud_instance cloud_volume cloud_network cloud_instance_list cloud_volume_list cloud_network_list cloud_service cloud_auth cloud_region get_cloud_instances_as_group get_cloud_regions get_cloud_availability_zones get_cloud_plans get_cloud_operating_systems cloud_image_list cloud_object get_cloud_floating_ip cloud_upload_key); Rex::Config->register_set_handler( "cloud" => sub { my ( $name, @options ) = @_; my $sub_name = "cloud_$name"; if ( $name eq "service" ) { cloud_service(@options); } if ( $name eq "auth" ) { cloud_auth(@options); } if ( $name eq "region" ) { cloud_region(@options); } } ); =head2 cloud_service($cloud_service) Define which cloud service to use. =over 4 =item Services =over 4 =item Amazon =item Jiffybox =item OpenStack =back =back =cut sub cloud_service { ($cloud_service) = @_; # set retry counter to a higher value if ( Rex::Config->get_max_connect_fails() < 5 ) { Rex::Config->set_max_connect_fails(15); } } =head2 cloud_auth($param1, $param2, ...) Set the authentication for the cloudservice. For example for Amazon it is: cloud_auth($access_key, $secret_access_key); For JiffyBox: cloud_auth($auth_key); For OpenStack: cloud_auth( tenant_name => 'tenant', username => 'user', password => 'password', ); =cut sub cloud_auth { @cloud_auth = @_; } =head2 cloud_region($region) Set the cloud region. =cut sub cloud_region { ($cloud_region) = @_; } =head2 cloud_instance_list Get all instances of a cloud service. task "list", sub { for my $instance (cloud_instance_list()) { say "Arch : " . $instance->{"architecture"}; say "IP : " . $instance->{"ip"}; say "ID : " . $instance->{"id"}; say "State : " . $instance->{"state"}; } }; There are some parameters for this function that can change the gathering of ip addresses for some cloud providers (like OpenStack). task "list", sub { my @instances = cloud_instance_list private_network => 'private', public_network => 'public', public_ip_type => 'floating', private_ip_type => 'fixed'; }; =cut sub cloud_instance_list { return cloud_object()->list_instances(@_); } =head2 cloud_volume_list Get all volumes of a cloud service. task "list-volumes", sub { for my $volume (cloud_volume_list()) { say "ID : " . $volume->{"id"}; say "Zone : " . $volume->{"zone"}; say "State : " . $volume->{"state"}; say "Attached : " . $volume->{"attached_to"}; } }; =cut sub cloud_volume_list { return cloud_object()->list_volumes(); } =head2 cloud_network_list Get all networks of a cloud service. task "network-list", sub { for my $network (cloud_network_list()) { say "network : " . $network->{network}; say "name : " . $network->{name}; say "id : " . $network->{id}; } }; =cut sub cloud_network_list { return cloud_object()->list_networks(); } =head2 cloud_image_list Get a list of all available cloud images. =cut sub cloud_image_list { return cloud_object()->list_images(); } =head2 cloud_upload_key Upload public SSH key to cloud provider private_key '~/.ssh/mykey public_key '~/.ssh/mykey.pub'; task "cloudprovider", sub { cloud_upload_key; cloud_instance create => { ... }; }; =cut sub cloud_upload_key { return cloud_object()->upload_key(); } =head2 get_cloud_instances_as_group Get a list of all running instances of a cloud service. This can be used for a I<group> definition. group fe => "fe01", "fe02", "fe03"; group ec2 => get_cloud_instances_as_group(); =cut sub get_cloud_instances_as_group { my @list = cloud_object()->list_running_instances(); my @ret; for my $instance (@list) { push( @ret, Rex::Group::Entry::Server->new( name => $instance->{"ip"} ) ); } return @ret; } =head2 cloud_instance($action, $data) This function controls all aspects of a cloud instance. =cut sub cloud_instance { my ( $action, $data ) = @_; my $cloud = cloud_object(); if ( $action eq "list" ) { return $cloud->list_running_instances(); } =head2 create Create a new instance. cloud_instance create => { image_id => "ami-xxxxxx", key => "ssh-key", name => "fe-ec2-01", # name is not necessary volume => "vol-yyyyy", # volume is not necessary zone => "eu-west-1a", # zone is not necessary floating_ip => "89.39.38.160" # floating_ip is not necessary }; =cut elsif ( $action eq "create" ) { my %data_hash = ( # image_id => $data->{"image_id"}, # name => $data->{"name"} || undef, # key => $data->{"key"} || undef, # zone => $data->{"zone"} || undef, # volume => $data->{"volume"} || undef, # password => $data->{"password"} || undef, # plan_id => $data->{"plan_id"} || undef, # type => $data->{"type"} || undef, # security_group => $data->{"security_group"} || undef, %{$data}, ); $cloud->run_instance(%data_hash); } =head2 start Start an existing instance cloud_instance start => "instance-id"; =cut elsif ( $action eq "start" ) { $cloud->start_instance( instance_id => $data ); } =head2 stop Stop an existing instance cloud_instance stop => "instance-id"; =cut elsif ( $action eq "stop" ) { $cloud->stop_instance( instance_id => $data ); } =head2 terminate Terminate an instance. This will destroy all data and remove the instance. cloud_instance terminate => "i-zzzzzzz"; =cut elsif ( $action eq "terminate" ) { $cloud->terminate_instance( instance_id => $data ); } } =head2 get_cloud_regions Returns all regions as an array. =cut sub get_cloud_regions { return cloud_object()->get_regions; } =head2 cloud_volume($action , $data) This function controlls all aspects of a cloud volume. =cut sub cloud_volume { my ( $action, @_data ) = @_; my $data; if ( @_data == 1 ) { if ( ref $_data[0] ) { $data = $_data[0]; } else { $data = { id => $_data[0] }; } } else { $data = { "id", @_data }; } my $cloud = cloud_object(); =head2 create Create a new volume. Size is in Gigabytes. task "create-vol", sub { my $vol_id = cloud_volume create => { size => 1, zone => "eu-west-1a", }; }; =cut if ( $action eq "create" ) { $cloud->create_volume( size => $data->{"size"} || 1, %{$data}, ); } =head2 attach Attach a volume to an instance. task "attach-vol", sub { cloud_volume attach => "vol-xxxxxx", to => "server-id"; }; =cut elsif ( $action eq "attach" ) { my $vol_id = $data->{id}; my $srv_id = $data->{to}; $cloud->attach_volume( volume_id => $vol_id, server_id => $srv_id, device_name => $data->{device} ); } =head2 detach Detach a volume from an instance. task "detach-vol", sub { cloud_volume detach => "vol-xxxxxx", from => "server-id"; }; =cut elsif ( $action eq "detach" ) { my $vol_id = $data->{id}; my $srv_id = $data->{from}; $cloud->detach_volume( volume_id => $vol_id, server_id => $srv_id, attach_id => $data->{attach_id} ); } =head2 delete Delete a volume. This will destroy all data. task "delete-vol", sub { cloud_volume delete => "vol-xxxxxx"; }; =cut elsif ( $action eq "delete" ) { $cloud->delete_volume( volume_id => $data->{id} ); } elsif ( $action eq "list" ) { return $cloud->list_volumes(); } } =head2 get_cloud_floating_ip Returns first available floating IP task "get_floating_ip", sub { my $ip = get_cloud_floating_ip; my $instance = cloud_instance create => { image_id => 'edffd57d-82bf-4ffe-b9e8-af22563741bf', name => 'instance1', plan_id => 17, floating_ip => $ip }; }; =cut sub get_cloud_floating_ip { return cloud_object()->get_floating_ip; } =head2 cloud_network =cut sub cloud_network { my ( $action, $data ) = @_; my $cloud = cloud_object(); =head2 create Create a new network. task "create-net", sub { my $net_id = cloud_network create => { cidr => '192.168.0.0/24', name => "mynetwork", }; }; =cut if ( $action eq "create" ) { $cloud->create_network( %{$data} ); } =head2 delete Delete a network. task "delete-net", sub { cloud_network delete => '18a4ccf8-f14a-a10d-1af4-4ac7fee08a81'; }; =cut elsif ( $action eq "delete" ) { $cloud->delete_network($data); } } =head2 get_cloud_availability_zones Returns all availability zones of a cloud services. If available. task "get-zones", sub { print Dumper get_cloud_availability_zones; }; =cut sub get_cloud_availability_zones { return cloud_object()->get_availability_zones(); } =head2 get_cloud_plans Retrieve information of the available cloud plans. If supported. =cut sub get_cloud_plans { return cloud_object()->list_plans; } =head2 get_cloud_operating_systems Retrieve information of the available cloud plans. If supported. =cut sub get_cloud_operating_systems { return cloud_object()->list_operating_systems; } =head2 cloud_object Returns the cloud object itself. =cut sub cloud_object { my $cloud = get_cloud_service($cloud_service); $cloud->set_auth(@cloud_auth); $cloud->set_endpoint($cloud_region); return $cloud; } 1;