Home > Ruby

Ruby

Wake on LAN in AWS

Thursday, March 06, 2014 Category : , , 2

Someone asked the question. Is Wake-on-LAN supported in Amazon Web Services.

The answer is no. But it also shows not thinking of infrastructure as code.

How would you approach this in AWS? You can fire of an API call to start any instance, but what if you wanted to make this easier? Simply tag your instances with an identifiable tag, such as "WakeOnLAN" and then run the following script (I prefer Ruby).

#!/usr/bin/ruby
require 'aws-sdk'

AWS.regions.sort_by(&:name).each do |region|
  next if region.name.match('cn-')
  puts region.name
  region.ec2.instances.each do |instance|
    if instance.status == :stopped and
       instance.tags.to_h.has_key?('WakeOnLAN')
      puts "\t#{instance.id} started"
      instance.start
    end
  end
end

That results in

[ec2-user@ ~]$ ./wake.rb 
ap-northeast-1
ap-southeast-1
ap-southeast-2
i-7c444f41 started
eu-west-1
sa-east-1
us-east-1
us-west-1
us-west-2
[ec2-user@ ~]$

The script simply goes through all of your instances in each region, finding those that have the WakeOnLAN tag and that are stopped, then starts them. If you run it make sure it has privilege to perform the actions, a role on an EC2 instance makes this easy.

I am a big fan of the AWS CLI too. Here is how to do the same on one line, all be it only within a single region. Its one command line but I have wrapped for formatting.

:~ rodos$ aws ec2 describe-instances 
--query 'Reservations[*].Instances[*].[InstanceId]'
--filters "Name=instance-state-name,Values=stopped"
"Name=tag-key,Values=WakeOnLAN" --output text 
| xargs aws ec2 start-instances --instance-ids
{
    "StartingInstances": [
        {
            "InstanceId": "i-7c444f41", 
            "CurrentState": {
                "Code": 0, 
                "Name": "pending"
            }, 
            "PreviousState": {
                "Code": 80, 
                "Name": "stopped"
            }
        }
    ]
}
:~ rodos$ 

This uses two very powerful features of the CLI. One is the --query option which lets you pull data out of the returned JSON data. The second is the --filters option which, as the name implies, lets you filter the results based on a lot of criteria. You can see all of the filters for the describe-instances command in the documentation. There are 78 different filters you can use (based on my quick count)!

Enjoy the world of infrastructure as code!

Rodos

Parse OnDemand pricing for AWS in Ruby

Sunday, August 04, 2013 Category : , 1

Well I have been doing a LOT more Ruby programming since my last "Hello World" post. I figured that I should probably start sharing some of the more useful code.

First is this class which gives you a quick and easy way to determine the current OnDemand pricing for an instance type. There is a Gem out there for this already but I wanting something that was small which I understood myself (thats how you learn). The class is not really useful by itself, but I am writing some analysis of Spot pricing (will post when its fully complete) and it really need to be able to dynamically get the current pricing.

What that the code does is pull the current pricing from the Amazon web site, which returns a json file. This file has a deep data structure that is really overboard for most usage. Also, the naming for instance types is very different to the normal 'm1.small' format. Therefore the code does a mapping between the two.

All you need to do is copy and paste the Class into your file and then instantiate the instance and use the price method to get a specific value. For example

puts OnDemandPricing.new.price('ap-southeast-2','t1.micro','linux')

Use at your own risk.

#!/usr/bin/ruby

require "rubygems"
require "net/http"
require "uri"
require "json"
require "yaml"

class OnDemandPricing

  REGION_MAPPING = {
    'us-east'    => 'us-east-1',
    'us-west'    => 'us-west-1',
    'us-west-2'  => 'us-west-2',
    'eu-ireland' => 'eu-west-1',
    'apac-tokyo' => 'ap-northeast-1',
    'apac-sin'   => 'ap-southeast-1',
    'apac-syd'   => 'ap-southeast-2',
    'sa-east-1'  => 'sa-east-1'}

  TYPE_MAPPING = {
    'uODI'            => 't1',
    'stdODI'          => 'm1',
    'secgenstdODI'    => 'm3',
    'hiMemODI'        => 'm2',
    'hiCPUODI'        => 'c1',
    'clusterComputeI' => 'cc1',
    'clusterHiMemODI' => 'cr1',
    'clusterGPUI'     => 'cg1',
    'hiIoODI'         => 'hi1',
    'hiStoreODI'      => 'hs1'}

  SIZE_MAPPINGS = {
    'u' => 'micro', 
    'sm' => 'small',
    'med' => 'medium',
    'lg' => 'large',
    'xl' => 'xlarge',
    'xxl' => '2xlarge',
    'xxxxl' => '4xlarge',
    'xxxxxxxxl' => '8xlarge'}

  def autovivifying_hash
    # A little helper to save initialising the hash as we go, make Ruby more like Perl!
    # http://en.wikipedia.org/wiki/Autovivification
    Hash.new do |hash, key|
      hash[key] = autovivifying_hash
    end
  end

  def initialize
    uri = URI.parse(
      "http://aws.amazon.com/ec2/pricing/pricing-on-demand-instances.json")
    request = Net::HTTP::Get.new(uri.request_uri)
    http = Net::HTTP.new(uri.host, uri.port)
    response = http.request(request)
    data = JSON.parse(response.body)

    @price_table = autovivifying_hash

    # Walk the json structure finding the key data and load into an easy to
    # access hash of hash of hashes
    # being price_table[REGION][INSTANCE TYPE][OS linux/mswin] returns price
    data['config']['regions'].each do |reg|
      reg['instanceTypes'].each do |type|
        type['sizes'].each do |size|
          size['valueColumns'].each do |val|
            @price_table[REGION_MAPPING[reg['region']]] \
                        [
                         TYPE_MAPPING[type['type']] +
                         "." + 
                         SIZE_MAPPINGS[size['size']] 
                        ] \
                        [val['name']] = val['prices']['USD']
          end
        end
      end 
    end
  end

  def price (region, instance_type, os)
    return @price_table[region][instance_type][os]
  end
end # Class

#An example
puts OnDemandPricing.new.price('ap-southeast-2','t1.micro','linux') 
Enjoy. Any feedback use the comments.

Hello World of AWS API with Ruby

Thursday, January 31, 2013 Category : , , , 4

After years of writing Perl I need to start learning Ruby. The most comprehensive SDK for Amazon Web Services (AWS) looks to be the Ruby SDK. It even contains interfaces for the new Elastic Transcoder service released overnight.

It is pleasing to find just how little it takes to create a "Hello AWS" style Ruby program. For my first test I decide that listing out my S3 buckets would be sufficient.

Here are four simple steps to get you started.

Step 1. Get Ruby on your machine.

Well of course you are using a recent Mac and Ruby is already installed. To confirm

machine:~ rodos$ ruby -v
ruby 1.8.7 (2012-02-08 patchlevel 358) [universal-darwin12.0]
machine:~ rodos$
Step 2. Install the AWS Ruby SDK 

This is a simple command
machine:~ rodos$ sudo gem install aws-sdk
Password:

Building native extensions.  This could take a while...
Building native extensions.  This could take a while...
Successfully installed uuidtools-2.1.3
Successfully installed nokogiri-1.5.6
Successfully installed json-1.7.6
Successfully installed aws-sdk-1.8.1.1
4 gems installed
Installing ri documentation for uuidtools-2.1.3...
Installing ri documentation for nokogiri-1.5.6...
No definition for parse_memory
No definition for parse_file
No definition for parse_with
No definition for get_options
No definition for set_options
Installing ri documentation for json-1.7.6...
Installing ri documentation for aws-sdk-1.8.1.1...
Installing RDoc documentation for uuidtools-2.1.3...
Installing RDoc documentation for nokogiri-1.5.6...
No definition for parse_memory
No definition for parse_file
No definition for parse_with
No definition for get_options
No definition for set_options
Installing RDoc documentation for json-1.7.6...
Installing RDoc documentation for aws-sdk-1.8.1.1...
machine:~ rodos$
Step 3. Create your .rb file with the code.

Create a file with the text below. I named my file "hello-aws.rb". Of course I still use vi for some silly reason to write code! You could always try pico or one of those fancy graphical text editors, even Xcode.
# List you S3 buckets

require 'rubygems'
require 'yaml'
require 'aws-sdk'

AWS.config(
:access_key_id => 'your.access.key.here',
:secret_access_key => 'your.secret.here')

s3 = AWS::S3.new

s3.buckets.each do |bucket|
puts bucket.name
end
Don't forget to enter your access key and secret for your account.

Step 4. Execute your file.

You can now run your code and watch it list your buckets.
machine:~ rodos$ ruby s3list.rb 
rodos.singapore.bucket1
rodos.singapore.bucket2
rodos.sydney.bucket1
machine:~ rodos$ 
There you go, it lists all of my buckets. From here it is developing your knowledge of the AWS SDK for Ruby alongside general Ruby programming skills. As I write some interesting code, I will share my experiences here.

Why don't you go and try your first automation of the Cloud with AWS and Ruby! Its fun and easy.

Rodos

P.S. It is a bad idea to leave your account access key and secret locked away in your code file. I have done it here to show a complete working example in a single file.

Powered by Blogger.