Saturday, April 13, 2013

Puppet manifests for cloudstack and co.

Here are some of my puppet manifests for Cloudstack and co. You can also find more information here:

https://github.com/oliverleach/cloudstack-puppet

I also recently did a presentation at the Cloudstack European meetup in London on the 11th April about deploying applications using puppet and Cloudstack. The talk was well received so I thought I would jot down the manifests that I wrote as part of the talk.

Here is the Cloudstack manifest:

# Class cloudstack
#
# This class installs cloudstack
#
# Author: Oliver Leach
# Date : 18th March 2013
#
# Actions:
# Installs the cloudstakc package
#
# This includes, the cloudstack-client, mysql-server and any install commands
#
# Currently the clodustack class is monlithic for demo purposes.
#
# Requires:
# Centos - other operating systems are not supported by this manifest
#
# Sample Usage:
# include cloudstack
#

class cloudstack ($mysql_password) {

 case $::operatingsystem {

  'centos': {
   $supported  = true
  }
 }

 if ($supported == true) {

  yumrepo { 'cloudstack':
      baseurl  => "http://cloudstack.apt-get.eu/rhel/4.0/",
   descr   => "Cloudstack 4.0.1 yum repo",
      enabled  => 1,
      gpgcheck => 0,
  }

  package { "cloud-client": 
   ensure  => "installed",
   require => Yumrepo["cloudstack"],
   }

  exec { "cloud-setup" :
   command => "/usr/bin/cloud-setup-databases cloud:cloud@localhost 
              --deploy-as=root:${mysql_password} && /bin/sleep 3 && 
              /usr/bin/cloud-setup-management && /bin/sleep 3 && 
              /usr/bin/cloud-setup-databases cloud:cloud@localhost 
              --deploy-as=root:${mysql_password} && /bin/sleep 3 && 
              /usr/bin/cloud-setup-management",
   creates => '/var/lib/mysql/cloud',
   require => Package["cloud-client"],
  }

        service { "cloud-management":
      ensure  => "running",
      enable  => "true",
      require => Exec["cloud-setup"],
  }
 }
}

MySQL server manifest


# Class myslqserver
#
# This class installs cloudstack
#
# Author: Oliver Leach
# Date : 20th March 2013
#
# Actions:
# Installs the mysql server package
#
# This includes, the mysql-server package
#
# Requires:
# Centos - other operating systems are not supported by this manifest
#
# Sample Usage:
# include mysql::server
#

class mysql::server ($mysql_password) {

 case $::operatingsystem {
  "centos": {
   $supported  = true
  }
 }

 if ($supported == true) {

        file { "flag-files" :

            ensure  => directory,
            path    => "/var/lib/puppet/flags/",
            owner   => root,
            group   => root,
            mode    => "0644",
        }

  package { "mysql-server":
   ensure  => installed,
   require => File["flag-files"], 
  }

        file { "/etc/my.cnf" :
            owner   => "root",
            group   => "root",
            source  => "puppet:///modules/mysql/my.cnf",
   require => Package["mysql-server"],
        }

  service { "mysqld" :
   enable  => true,
   ensure  => running,
         require => File["/etc/my.cnf"],
  }

        $mysql_server_exec_1 = "0000000000001"
        $mysql_server_file_1 = "/var/lib/puppet/flags/mysql_server_exec_1"

  exec { "restart-mysqld" :
            command => "/sbin/service mysqld restart && echo 
                       \"$mysql_server_exec_1\" > \"$mysql_server_file_1\"",
            unless  => "/usr/bin/test \"`/bin/cat $mysql_server_file_1 
                       2>/dev/null`\" = \"$mysql_server_exec_1\"",
            require => Service["mysqld"],
            }

  exec { "set-mysql-password" :
   unless  => "/usr/bin/mysqladmin -uroot -p${mysql_password} status",
   command => "/usr/bin/mysqladmin -uroot password ${mysql_password}",
   require => Exec["restart-mysqld"],
  }

  $mysql_server_exec_2 = "0000000000001"
  $mysql_server_file_2 = "/var/lib/puppet/flags/mysql_server_exec_2"  

  exec { "remove-test-dbs" :
   command => "/usr/bin/mysql -u root -p${mysql_password} -e 
              \"DELETE FROM mysql.db WHERE Db='test' OR Db='test
              \\_%';\" && echo \"$mysql_server_exec_2\" > 
              \"$mysql_server_file_2\"",
   unless  => "/usr/bin/test \"`/bin/cat $mysql_server_file_2 2>
              /dev/null`\" = \"$mysql_server_exec_2\"",
   require => Exec["set-mysql-password"],
   }

     $mysql_server_exec_3 = "0000000000003"
        $mysql_server_file_3 = "/var/lib/puppet/flags/mysql_server_exec_3"

  exec { "remove-remote-root-access" :
   command => "/usr/bin/mysql -u root -p${mysql_password} -e 
              \"DELETE FROM mysql.user WHERE User='root' AND 
              Host NOT IN ('localhost', '127.0.0.1', '::1');
              \" && echo \"$mysql_server_exec_3\" > \
              "$mysql_server_file_3\"",
   unless  => "/usr/bin/test \"`/bin/cat $mysql_server_file_3 
              2>/dev/null`\" = \"$mysql_server_exec_3\"",
   require => Exec["set-mysql-password"],
  }
 }
}


Apache manifest


class apache {

 $snat_ip = $snat_ipaddress

 case $operatingsystem {

  'CentOS', 'RedHat' : {
   $supported = true
  }
 } 

 if ($supported == true) {

  file { 'httpd_conf_d' :
   ensure  => directory,
         path   => '/etc/httpd/conf.d/',
         recurse => true,
         purge   => true,
         force   => true,
         owner   => "root",
         group   => "root",
         mode    => 0644,
   require => Package['httpd'],
  }

  file { 'cloudstack_conf' :

   path => '/etc/httpd/conf.d/cloudstack.conf',
   owner  => root,
   group => root,
   mode  => '0644',
   content => template('apache/cloudstack.conf.erb'),
   require => File['httpd_conf_d'],
  }

        package { 'httpd' :
   ensure  => present,
  }

        package { 'mod_ssl' : 
   ensure  => present,
  }

       service { 'httpd' :
         ensure  => 'running',
         enable  => true,
   require => File['cloudstack_conf'],
  }
 }
} 
 
and the ERB template: 

LoadModule ssl_module modules/mod_ssl.so

NameVirtualHost *:80

<VirtualHost *:80>
   ServerName <%= snat_ip %>
   DocumentRoot /var/www/html/
   Redirect permanent / https://<%= snat_ip %>/client
</VirtualHost>


Listen 443
NameVirtualHost *:443
<VirtualHost *:443>
   SSLEngine On
   #SSLCertificateFile /var/lib/puppet/ssl/certs/ca.pem
   #SSLCertificateKeyFile /var/lib/puppet/ssl/certs/cert001.pem

   SSLCertificateFile /etc/pki/tls/certs/localhost.crt
   SSLCertificateKeyFile /etc/pki/tls/private/localhost.key

   RewriteEngine On
   ProxyRequests On
   ProxyVia On
   <Proxy *>
     Order deny,allow
     Deny from all
     Allow from all
   </Proxy>

   Redirect permanent / /client

   ProxyPass /client http://localhost:8080/client
   ProxyPassReverse /client http://localhost:8080/client

</VirtualHost> 
 
And the iptables manifest

class iptables {
 
    $ipv4_file = $operatingsystem ? {
        "debian"          => '/etc/iptables/rules.v4',
        /(RedHat|CentOS)/ => '/etc/sysconfig/iptables',
    }
 
    exec { "purge default firewall":
        command => "/sbin/iptables -F && /sbin/iptables-save > 
                   $ipv4_file && /sbin/service iptables restart",
        onlyif  => "/usr/bin/test `/bin/grep \"Firewall configuration 
                   written by\" $ipv4_file | /usr/bin/wc -l` -gt 0",
        user    => 'root',
    }
 
    /* Make the firewall persistent */
    exec { "persist-firewall":
        command     => "/bin/echo \"# This file is managed by puppet. 
                       Do not modify manually.\" > $ipv4_file && 
                       /sbin/iptables-save >> $ipv4_file", 
        refreshonly => true,
        user        => 'root',
    }
 
    /* purge anything not managed by puppet */
    resources { 'firewall':
        purge => true,
    }
 
    firewall { '000 INPUT allow related and established':
        state  => ['RELATED', 'ESTABLISHED'],
        action  => 'accept',
        proto  => 'all',
    }
 
    firewall { "001 accept all icmp requests":
        proto  => 'icmp',
        action  => 'accept',
    }
 
    firewall { '002 INPUT allow loopback':
        iniface => 'lo',
        chain   => 'INPUT',
        action  => 'accept',
    }

    firewall { '100 allow ssh':
        state  => ['NEW'],
        dport  => '22',
        proto  => 'tcp',
        action  => 'accept',
    }
    firewall { '100 allow port 80':
        state  => ['NEW'],
        dport  => '80',
        proto  => 'tcp',
        action  => 'accept',
    }
    firewall { '100 allow port 443':
        state  => ['NEW'],
        dport  => '443',
        proto  => 'tcp',
        action  => 'accept',
    }
    firewall { '100 allow 9090':
        state  => ['NEW'],
        dport  => '9090',
        proto  => 'tcp',
        action  => 'accept',
    }
 
    firewall { '100 allow 8250':
        state  => ['NEW'],
        dport  => '8250',
        proto  => 'tcp',
        action  => 'accept',
    }

    firewall { '100 allow 7080':
        state  => ['NEW'],
        dport  => '7080',
        proto  => 'tcp',
        action  => 'accept',
    }

    firewall { '100 allow 8080':
        state  => ['NEW'],
        dport  => '8080',
        proto  => 'tcp',
        action => 'accept',
    }

    firewall { "998 deny all other requests":
        action  => 'reject',
        proto  => 'all',
        reject  => 'icmp-host-prohibited',
    }
 
    firewall { "999 deny all other requests":
        chain   => 'FORWARD',
        action  => 'reject',
        proto   => 'all',
        reject  => 'icmp-host-prohibited',
    }
}
 


Wednesday, March 20, 2013

Cloudstack 4.1 - Regions are coming to town!

 







Here a list of new features in Cloudstack 4.1 from a presentation that Chip Chandler gave.











 


There are a few new features, but I though I would talk about AWS style regions:

Uhem.. AWS style regions you say? This is a BIG move. It's taking Cloudstack zones and adding a new layer on top. Well why would you want to do that? Cloudstack zones do not spread well geographically. In fact multiple Cloudstack zones are often deployed within a data center to work around the 4095 vlan limit by keep the vlans within a rack and using L3 routing to move to another rack. That is ok, but customers cannot easily route between zones (yet). Having regions will allow additional segregation, with one important new feature which is splitting up the Cloudstack management server, so each CSM will manage one region. This is perfect is your customers are spread geographically.

Here are the requirements around regions in Cloudstack:

• Admin should be able to install a management server for a Region and then connect to management servers in other Regions. Each Management Server in each region should know about all other regions and their management servers. The following operation allows for each CSM server to know about the other CSM in the other region:
 

Connect To Region
 

• The CS Admin should be able to create multiple Regions within a CloudStack cloud. I.e., Cloudstack should manage multiple regions within a cloud. The following operations should be supported:
 

Create Region
Delete Region
List Regions
 

• Admin should be able to create multiple zones within a Region. Management server cluster manages all the zones within the region. Following Zone operations should be supported on per regions basis:

Create Zone
Delete Zone
List Zones

• Management Server cluster for each region should only control the infrastructure within
that region. In other words, if a management server cluster fails, then only the region managed by that server cluster should be inaccessible for admin and users. It should not have any impact on the other Regions in the cloud
 

• Each Region should have access to an object store. The object store should be common to all Zones within the Region. I.e., any object stored from a Zone should be visible across
all the other zones within the region
 

• EIP function should be available across both basic and advance Zones within a Region
 

• ELB function should be available across both basic and advance Zones within a Region
 

• The administrative hierarchy  (Users, Accounts, Domains, Projects) - should be valid across all the regions. I.e., if admins create domains, accounts and users etc. in one region it should be reflected in other regions as well.

This is key. I create a user in AZ 1 and I want that user to be able to create a VM in AZ2 using the same credentials, api and secret key.

• Authentication credentials – username, password, keys – should be valid across all
regions


^ Oh, I just said that! ^

• Switching between Regions should not require the user to sign-on again (SSO should be supported).

• Resource management should be extended to Region level
 

Available compute, storage, network (IPs, VLANs etc.) resources that are currently tracked
should be aggregated and displayed at region level


Appropriate global parameters should be available at Region level while the remaining would be available at Zone level
 

• Usage: All the (per account) usage records should be aggregated to Regions level

This is important for billing purposes.

• Global Configurations: All of the administrative hierarchy (Domains, Accounts, Users, Projects) related Global Configurations should have consistent values across all regions and has to be propagated when a user modifies a configuration on one of the regions.
• Each region should maintain a list of all other regions and their region endpoints.


As you can see, this really is a big change. How people will be able to upgrade will also be a big area of work. However this is way the product needs to move to ensure that it can keep delivering a top class service.

Here are a couple of links that you can look at to find out how the guys did it. This is why opensource is so great. No hiding, which means we can all get a better idea of how this beast will work

https://issues.apache.org/jira/browse/CLOUDSTACK-241

https://issues.apache.org/jira/browse/CLOUDSTACK-815