Cloud-init script examples for Cloudforms

Example #1 (tested on Openstack) – Simple script to register a VM to Satellite 6 and IdM. Also allow root login without key and setting password to “password123”

#cloud-config
# vim:syntax=yaml
debug: True
ssh_pwauth: True
disable_root: false
chpasswd:
  list: |
    root: password123
  expire: false
runcmd:
- sed -i'.orig' -e's/without-password/yes/' /etc/ssh/sshd_config
- service sshd restart
- yum -y localinstall http://satellite6.home.marcoberube.com/pub/katello-ca-consumer-latest.noarch.rpm >> /root/cloudinit.log
- subscription-manager register --org MarcoBerube --activationkey rhel7-basic >> /root/cloudinit.log
- yum -y install ipa-client >> /root/cloudinit.log
- ipa-client-install --enable-dns-updates --mkhomedir -p admin -w password123 --unattended >> /root/cloudinit.log
- yum -y install katello-agent >> /root/cloudinit.log
- yum -y update && yum clean all >> /root/cloudinit.log
- systemctl enable goferd.service >> /root/cloudinit.log
- yum -y install puppet >> /root/cloudinit.log
- echo server=satellite6.yourdomain.com >> /etc/puppet/puppet.conf
- systemctl enable puppet >> /root/cloudinit.log
- systemctl restart puppet >> /root/cloudinit.log
- reboot

Example #2 (tested on AWS) – This example shows how to pass arguments from Cloudforms to your cloud-init script dynamically.

#cloud-config
# MiqProvisionAmazon_Web.yaml
# CloudForms - Cloud-Init Script for Apache Installation on EC2
# For troubleshooting check: /var/lib/cloud/instance/user-data.txt & /var/log/boot.log

<%   # Set Global Variables   
    role                = evm[:role]   
    role                ||= evm[:ws_values][:role] rescue 'web'   
    instance_name       = evm[:vm_target_name]   
    key_pair            = evm[:keypair]   
    key_pair            ||= evm[:ws_values][:keypair] rescue nil %>

bootcmd:
  ## Turn off SELinux
  - setenforce 0

ssh_authorized_keys:
    <% if key_pair.nil? %>
  - echo "WARN --: Using default key pari"
    <% else %>
  - <%=key_pair%>
    <% end %>

runcmd:
  ## Setup motd and root authorized keys
  - echo Welcome to Red Hat CloudForms <%=role.titlecase%> Instance <%=instance_name%> > /etc/motd

  ## Turn off firewall
  - echo "CloudForms - Turning off firewall"
  - chkconfig iptables off  
  - service iptables stop

  ## Install Apache
  - echo "CloudForms - Installing apache"
  - yum -y install httpd wget
  - service httpd start
  - chkconfig httpd on

  ## Modify default apache homepage
  - echo "CloudForms - Modifyig default apache homepage"
  - sed -i 's/Red Hat Enterprise Linux.  Welcome to Red Hat CloudForms <%=role.titlecase%> Instance <%=instance_name%>/' /var/www/error/noindex.html

Cloudforms 3.1 Enhancements

Cloudforms 3.1 has just been released.   Here are a few enhancements:

  • REST API Integration:  Cloudforms Management Engine now offers a REST API that supports Service Management and Insight capabilities
  • Openstack Enhanced Integration:  Ability to gather information from Glance, Cinder and Neutron services.
  • Support for Microsoft SCVMM:  Tech preview!  Full support coming soon.

For more information, download the release form here.

 

Naming VMs based on Group name

One of my customer asked me to name his VMs based on the group name provisioning that VM.  Example:  redgroup-001

Turns out that you cannot use the usual naming method as services are always provisioned as “super-administrator” in Cloudforms 3.0.

I added the following code in my “CustomizeRequest” to get this done:

  1.   #
  2.   # Changing VM name based on Group:  groupname_###
  3.   #
  4.  
  5.   miq_request = prov.miq_request
  6.   user = miq_request.requester
  7.   group = user.miq_group
  8.  
  9.   log(:info, “User: #{user.name} Group: #{group.description}”)
  10.   vm_name = “#{group.description}_”
  11.    
  12.     # Loop through 0-999 and look to see if the name already exists in CFME
  13.     for i in (1..999)
  14.       new_vm_name = “#{vm_name}#{i}”
  15.       if $evm.vmdb(‘vm’).find_by_name(new_vm_name).nil?
  16.         log(:info, “Using VM:<#{new_vm_name}> name”)
  17.         break
  18.       end
  19.     end
  20.   prov.set_option(:vm_target_name, new_vm_name)
  21.   log(:info, “Adding Option prov.set_option(:vm_target_name, #{prov.get_option(:vm_target_name)}) to Provisioning ID:<#{prov.id}>”)
  22.   prov.set_option(:vm_target_hostname, new_vm_name)
  23.   log(:info, “Adding Option prov.set_option(:vm_target_hostname, #{prov.get_option(:vm_target_hostname)}) to Provisioning ID:<#{prov.id}>”)
  24.   prov.set_option(:vm_name, new_vm_name)
  25.   log(:info, “Adding Option prov.set_option(:vm_name, #{prov.get_option(:vm_name)}) to Provisioning ID:<#{prov.id}>”)

Export a sub-section of your automation model

In Cloudforms 3.0, you can only export the full automation model from the web interface.  That said, what if you want to take a backup of only a sub-section.

Here is how to do it from the appliance command line:

# cd /var/www/miq/vmdb/
# script/rails runner script/rake evm:automate:export NAMESPACE=<your_namespace> CLASS=<class_name> FILE=<filename>.xml

To export all classes under an namespace, just remove your class argument.

Call or SMS notification using Twilio

By default, Cloudforms notify a user by email that a new VM or a Service has been fully provisioned.  What if you would like to send a text message (SMS) or call somebody?  Twilio let you call or SMS somebody from a Restful API.

Step 1)  Create a Twilio account

Twilio | Try Twilio Free

Please note that if you are using the FREE service, you will get a twilio trial message before your SMS or CALL.  To remove this message, you will have to pay for this service.  That said, it’s very cheap!

Step 2)  Install required Twilio Ruby Gems on your appliance
gem install twilio-ruby
gem install twilio-twimlbin
Step 3) Import my Twilio class in your automation model

Download here (unzip before importing in Cloudforms):  Twilio.xml.zip

** By default, my Twilio class will be imported under /POC/Integration namespace.  Edit the XML file if want to change the location.

Here is what the Twilo-call code looks like.  Very simple…

  1. ###################################
  2. #
  3. # CFME Automate Method: twilio-call
  4. #
  5. # by Marco Berube
  6. #
  7. # Notes: Twilio is a service to send call or sms request from a restful API.
  8. #            More informatin at:  https://www.twilio.com/api
  9. #
  10. ###################################
  11.  
  12. begin
  13.   # Method for logging
  14.   def log(level, message)
  15.     @method = ‘twilio-call’
  16.     $evm.log(level, “#{@method}: #{message}”)
  17.   end
  18.   # dump_root
  19.   def dump_root()
  20.     log(:info, “Root:<$evm.root> Begin $evm.root.attributes”)
  21.     $evm.root.attributes.sort.each { |k, v| log(:info, “Root:<$evm.root> Attribute – #{k}: #{v}”)}
  22.     log(:info, “Root:<$evm.root> End $evm.root.attributes”)
  23.     log(:info, “”)
  24.   end
  25.  
  26.   # dump all root attributes to the log
  27.   dump_root
  28.  
  29. require ‘twilio-twimlbin’
  30. require ‘twilio-ruby’
  31.  
  32. sid = $evm.object[‘sid’]
  33. token = $evm.object[‘token’]
  34. from_number = $evm.object[‘from’]
  35. to_number = $evm.object[‘to’]
  36. to_number ||= $evm.root[‘dialog_twilio_to’] 
  37. body = $evm.object[‘message’]
  38.  
  39.  
  40. response = Twilio::TwiML::Response.new do |r|
  41.   r.Say body, :voice => ‘woman’
  42. end
  43.  
  44. twiml = Twimlbin.new
  45. twiml.create(response.text)
  46.  
  47. # set up a client to talk to the Twilio REST API 
  48. @client = Twilio::REST::Client.new sid, token 
  49.  
  50. @client.account.calls.create({
  51.   :to => to_number, 
  52.   :from => from_number, 
  53.   :url => twiml.external_url,  
  54.   :method => ‘GET’,  
  55.   :fallback_method => ‘GET’,  
  56.   :status_callback_method => ‘GET’,    
  57.   :record => ‘false’
  58. })
  59.  
  60.   # Exit method
  61.   log(:info, “Twilio notification call request completed”)
  62.   exit MIQ_OK
  63.  
  64.   # Error Rescue
  65. rescue => err
  66.   log(:error, “[#{err}]\n#{err.backtrace.join(“\n“)}”)
  67.   exit MIQ_STOP
  68. end
Step 4) Update your Twilio attributes for your CALL and SMS instance. 

screenshot-twilio

Sid / Token =  your username/password to access your Twilio account from a Restful API.  Log in to your Twilio account to find this information

From = Your Twilio account phone number.  Log in to your Twilio account to find this information

To = Default phone number that this class will call to notify the user.  You can dynamically change this phone number from your service dialog by using a variable called “twilio_to”.  This call will overwrite the default number from dialog_twilio_to

Message = Your notification message

* Obviously, there is a lot of potential improvements here.    I am currently using this only on demos, so I didn’t spend much time on this.  But you could get the user phone number from active directory or provide the VM name in your notification message.

Step 5) Replace your default Email class by this Twilio/Call or SMS at the end of your provisioning state machine.