virtualmaster 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,6 @@
1
+ *.gem
2
+ .bundle
3
+ Gemfile.lock
4
+ pkg/*
5
+ vendor/bundle
6
+ vendor/cache
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in vmaster.gemspec
4
+ gemspec
data/README.mdown ADDED
@@ -0,0 +1,55 @@
1
+ # VirtualMaster command line interface
2
+
3
+ Proof of concept of VirtualMaster command line interface
4
+
5
+ # virtualmaster config
6
+ Running virtualmaster for first time.
7
+ Setting up environment.
8
+
9
+ Your API credentials are available from http://www.virtualmaster.cz/en/api#settings
10
+
11
+ Enter your API username:
12
+ Enter your API password:
13
+
14
+ Settings stored within ~/.virtualmaster
15
+
16
+ # virtualmaster create demo1 --image lucid
17
+ Using image 'lucid' with id (999).
18
+ Creating 'micro' instance (512 MB memory/5 GB storage).
19
+ Instance launch request accepted (instance id 44444).
20
+
21
+ Default password 'AdVc:PBi8&7L'.
22
+
23
+ # virtualmaster instances
24
+ +-------+---------+----------------+
25
+ | name | state | ip_address |
26
+ +-------+---------+----------------+
27
+ | demo1 | RUNNING | 195.140.253.88 |
28
+ +-------+---------+----------------+
29
+
30
+ ## Install
31
+
32
+ The current version of `virtualmaster` command line interface should work with Ruby 1.8.7+.
33
+
34
+ ## Automatically install SSH keys
35
+
36
+ VirtualMaster CLI can install your SSH keys to a remote machine automatically using `--copy-id` switch.
37
+
38
+
39
+ virtualmaster create demo1 --image ubuntu_lucid --copy-id
40
+ Using image 'ubuntu_lucid' with ID 124
41
+ Creating 'micro' instance (512 MB memory/10 GB storage)
42
+ Instance launch request accepted. Instance ID 45387
43
+
44
+ Default password 'vBK7i!kK'
45
+ Waiting for instance............
46
+ Loading identity file
47
+
48
+ Instance ready!
49
+ Try to login using `ssh root@195.140.253.130'
50
+
51
+ If you want to specify other key (ie. not ~/.ssh/id_rsa) use option `--identity IDENTITY_FILE`.
52
+
53
+ ## More information
54
+
55
+ Additional topics are available in [the wiki](https://github.com/Virtualmaster/virtualmaster-cli/wiki).
data/Rakefile ADDED
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
data/bin/virtualmaster ADDED
@@ -0,0 +1,22 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ begin
4
+ require 'vmaster'
5
+ rescue LoadError
6
+ vmaster_path = File.expand_path('../../lib', __FILE__)
7
+
8
+ $:.unshift(vmaster_path)
9
+
10
+ require 'vmaster'
11
+ end
12
+
13
+ program :name, "virtualmaster"
14
+ program :version, VirtualMaster::VERSION
15
+ program :description, "VirtualMaster command line interface"
16
+ program :help_formatter, :compact
17
+
18
+ default_command :help
19
+
20
+ VirtualMaster::CLI.run do
21
+ end
22
+
data/lib/vmaster.rb ADDED
@@ -0,0 +1,9 @@
1
+ require "vmaster/version"
2
+ require "vmaster/helpers"
3
+ require "vmaster/cli"
4
+ require "commander/import"
5
+
6
+ # include commands
7
+ require 'vmaster/config_command'
8
+ require 'vmaster/server_commands'
9
+
@@ -0,0 +1,32 @@
1
+ require 'yaml'
2
+ require 'deltacloud'
3
+
4
+ module VirtualMaster
5
+ class CLI
6
+ @@api = nil
7
+ @@config = nil
8
+
9
+ def self.run
10
+ # load config
11
+ config_file = File.join(ENV["HOME"], ".virtualmaster")
12
+ if File.exists? config_file
13
+ config = YAML::load(File.open(config_file))
14
+
15
+ @@config = config.inject({}){|memo,(k,v)| memo[k.to_sym] = v; memo}
16
+
17
+ @@api = DeltaCloud.new(@@config[:username], @@config[:password], VirtualMaster::DEFAULT_URL)
18
+ end
19
+
20
+ yield
21
+ end
22
+
23
+ def self.api
24
+ abort "No configuration available! Please run 'virtualmaster config' first!" unless @@api
25
+ @@api
26
+ end
27
+
28
+ def self.config
29
+ @@config
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,50 @@
1
+ require 'yaml'
2
+
3
+
4
+ command :config do |c|
5
+ c.description = "Configure VirtualMaster API username and password"
6
+
7
+ c.option '--username NAME', String, 'API username'
8
+ c.option '--password PASSWD', String, 'API password'
9
+
10
+ c.action do |args, options|
11
+ config_file = File.join(ENV['HOME'], VirtualMaster::CONFIG_FILE)
12
+ if File.exists?(config_file)
13
+ abort "Default configuration file already exists: #{config_file}"
14
+ else
15
+ say "Running virtualmaster for first time"
16
+ end
17
+
18
+ unless options.username && options.password
19
+ say "\n"
20
+ say "Your API credentials are available from http://www.virtualmaster.cz/en/api#settings"
21
+ say "\n"
22
+ end
23
+
24
+ options.username = ask("Enter API username:") unless options.username
25
+ options.password = password("Enter API password:", "*") unless options.password
26
+
27
+ # verify and store credentials
28
+ begin
29
+ api = DeltaCloud.new(options.username, options.password, VirtualMaster::DEFAULT_URL)
30
+
31
+ config = {
32
+ 'username' => options.username,
33
+ 'password' => options.password,
34
+ 'default_image' => VirtualMaster::DEFAULT_IMAGE,
35
+ 'images' => VirtualMaster::IMAGES
36
+ }
37
+
38
+ File.open(config_file, 'w') do |f|
39
+ f.puts YAML.dump(config)
40
+ end
41
+
42
+ say "Setting stored under #{config_file}"
43
+ rescue DeltaCloud::API::BackendError => e
44
+ say "Unable to connect to VirtualMaster API: #{e.message}"
45
+ rescue Exception => e
46
+ say "Unable to configure environment: #{e.message}"
47
+ end
48
+
49
+ end
50
+ end
@@ -0,0 +1,71 @@
1
+ module VirtualMaster
2
+
3
+ CONFIG_FILE = ".virtualmaster"
4
+ DEFAULT_URL = "https://www.virtualmaster.cz/services/deltacloud"
5
+
6
+ DEFAULT_IMAGE = 124
7
+ DEFAULT_PROFILE = "micro"
8
+
9
+ # pre-defined list of images
10
+ IMAGES = {
11
+ :ubuntu_lucid => 124,
12
+ :debian_squeeze => 1741,
13
+ :centos_6 => 1743
14
+ }
15
+
16
+ PROFILES = {
17
+ :nano => {
18
+ :memory => 256,
19
+ :storage => 3840
20
+ },
21
+ :micro => {
22
+ :memory => 512,
23
+ :storage => 6144
24
+ },
25
+ :milli => {
26
+ :memory => 1024,
27
+ :storage => 20480,
28
+ },
29
+ :small => {
30
+ :memory => 2048,
31
+ :storage => 30720
32
+ },
33
+ :medium => {
34
+ :memory => 4096,
35
+ :storage => 40960
36
+ }
37
+ #:large => {
38
+ # :memory => 8192,
39
+ # :storage => 8192
40
+ #}
41
+ }
42
+
43
+
44
+ module Helpers
45
+ def self.get_instances
46
+ VirtualMaster::CLI.api.instances
47
+ end
48
+
49
+ def self.get_instance(name)
50
+ get_instances.each do |instance|
51
+ return instance if instance.name == name
52
+ end
53
+
54
+ nil
55
+ end
56
+
57
+ def self.get_hw_profile(memory, storage)
58
+ api = VirtualMaster::CLI.api
59
+
60
+ profile_list = api.hardware_profiles.reject { |p| p.memory.value.to_i != memory && p.storage.value.to_i != storage }
61
+
62
+ profile_list.first
63
+ end
64
+
65
+ def self.create_instance(name, image_id, profile_id)
66
+ api = VirtualMaster::CLI.api
67
+
68
+ api.create_instance(image_id, :name => name, :hwp_id => profile_id)
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,163 @@
1
+ require 'terminal-table'
2
+ require 'net/ssh'
3
+ require 'logger'
4
+ require "base64"
5
+ require 'openssl'
6
+
7
+ log = Logger.new(STDOUT)
8
+ log.level = Logger::WARN
9
+
10
+ # SSH copy id
11
+ # FIXME how to select the right key
12
+
13
+ command :create do |c|
14
+ c.description = "Launch new server instance"
15
+ c.option '--image TEMPLATE', String, 'instance template to use'
16
+ c.option '--profile PROFILE', String, 'instance hardware profile'
17
+ c.option '--copy-id', 'install public key on a machine'
18
+ c.option '--identity IDENTITY', String, 'SSH identity to use (with --copy-id)'
19
+ c.option '--wait', 'wait for instance to become operational'
20
+ c.action do |args, options|
21
+ # default values
22
+ options.default :identity => File.join(ENV['HOME'], '.ssh/id_rsa')
23
+
24
+ name = args.shift || abort('Server name required')
25
+
26
+ # verify server name
27
+ abort("Virtual server with name #{name} already exists!") if VirtualMaster::Helpers.get_instance(name)
28
+
29
+ # image
30
+ image_name = nil
31
+ image_id = VirtualMaster::CLI.config[:default_image] || VirtualHost::DEFAULT_IMAGE
32
+
33
+ if options.image
34
+ image_name = options.image
35
+
36
+ if image_name.match /^id:/
37
+ # use image_id directly
38
+ image_id = image_name[3..-1].to_i
39
+
40
+ image_name = nil
41
+ else
42
+ # lookup predefined images
43
+ image_id = VirtualMaster::CLI.config[:images][image_name.to_sym]
44
+
45
+ abort "Image '#{image_name}' not recognized!" unless image_id
46
+ end
47
+ end
48
+
49
+ say image_name ? "Using image '#{image_name}' with ID #{image_id}" : "Using image with ID #{image_id}"
50
+
51
+ # instance hardware profile
52
+ profile_name = options.profile || VirtualMaster::DEFAULT_PROFILE
53
+
54
+ profile = VirtualMaster::PROFILES[profile_name.to_sym]
55
+ abort "Image name '#{options.profile}' not recognized!" unless profile
56
+
57
+ hwp = VirtualMaster::Helpers.get_hw_profile(profile[:memory], profile[:storage])
58
+ abort "Internal error: hardware profile not available" unless hwp
59
+
60
+ say "Creating '#{profile_name}' instance (#{profile[:memory]} MB memory/#{profile[:storage]/1024} GB storage)"
61
+
62
+ instance = VirtualMaster::Helpers.create_instance(name, image_id, hwp.id)
63
+
64
+ # TODO handle exceptions (invalid image/profile, limits, etc.)
65
+
66
+ say "Instance launch request accepted. Instance ID #{instance.id}"
67
+
68
+ # FIXME authentication is missrepresented within Ruby object
69
+ password = instance.authentication[:username]
70
+ say "\n"
71
+ say "Default password '#{password}'"
72
+
73
+ # copy-id implies waiting for instance to become operational
74
+ if options.wait || options.copy_id
75
+ print 'Waiting for instance'
76
+
77
+ while (instance = VirtualMaster::Helpers.get_instance(name)).state != "RUNNING" do
78
+ print '.'
79
+
80
+ sleep(5)
81
+ end
82
+
83
+ puts
84
+
85
+ # copy ssh id
86
+ if options.copy_id
87
+ authorized_key = nil
88
+
89
+ abort "Specified identity file #{options.identity} doesn't exists!" unless File.exist?(options.identity)
90
+
91
+ say "Loading identity file\n"
92
+ key = OpenSSL::PKey::RSA.new File.read options.identity
93
+
94
+ # build authorized key output string
95
+ authtype = key.class.to_s.split('::').last.downcase
96
+ b64pub = ::Base64.encode64(key.to_blob).strip.gsub(/[\r\n]/, '')
97
+ authorized_key = "ssh-%s %s\n" % [authtype, b64pub] # => ssh-rsa AAAAB3NzaC1...=
98
+
99
+ Net::SSH.start(instance.public_addresses.first[:address], 'root', :password => password) do |ssh|
100
+ # TODO exception handling
101
+ output = ssh.exec!("mkdir ~/.ssh")
102
+ output = ssh.exec!("echo '#{authorized_key}' >>~/.ssh/authorized_keys")
103
+ end
104
+ end
105
+
106
+ puts
107
+ puts "Instance ready!"
108
+ puts "Try to login using `ssh root@#{instance.public_addresses.first[:address]}'"
109
+ end
110
+ end
111
+ end
112
+
113
+ command :list do |c|
114
+ c.description = "List all running servers"
115
+ c.action do |args, options|
116
+ instances = []
117
+
118
+ VirtualMaster::Helpers.get_instances.each do |instance|
119
+ unless instance.public_addresses.first.nil?
120
+ ip_address = instance.public_addresses.first[:address]
121
+ else
122
+ ip_address = "(not assigned)"
123
+ end
124
+
125
+ instances << [instance.name, instance.state, ip_address]
126
+ end
127
+
128
+ abort "No instances found" if instances.empty?
129
+
130
+ table = Terminal::Table.new :headings => ['name','state','ip_address'], :rows => instances
131
+ puts table
132
+ end
133
+ end
134
+
135
+ def instance_action(action, args)
136
+ name = args.shift || abort('server name required')
137
+
138
+ instance = VirtualMaster::Helpers.get_instance(name)
139
+ instance.send("#{action}!")
140
+ end
141
+
142
+ %w{start reboot stop shutdown destroy}.each do |cmd|
143
+ command cmd do |c|
144
+ c.syntax = "virtualmaster #{c.name} SERVER"
145
+
146
+ case c.name
147
+ when "start"
148
+ c.description = "Start server (when stopped)"
149
+ when "reboot"
150
+ c.description = "Reboot server"
151
+ when "stop"
152
+ c.description = "Stop server"
153
+ when "shutdown"
154
+ c.description = "Shutdown server (ACPI)"
155
+ when "destroy"
156
+ c.description = "Remove server"
157
+ end
158
+
159
+ c.action do |args, options|
160
+ instance_action(c.name, args)
161
+ end
162
+ end
163
+ end
@@ -0,0 +1,3 @@
1
+ module VirtualMaster
2
+ VERSION = "0.0.3"
3
+ end
@@ -0,0 +1,17 @@
1
+ require 'spec_helper'
2
+
3
+ describe "VirtualMaster commands" do
4
+ before :each do
5
+ @runner = Commander::Runner.instance
6
+ end
7
+
8
+ it "should have command :config" do
9
+ @runner.commands.should have_key('config')
10
+ end
11
+
12
+ it "should have command server commands" do
13
+ %w{create list start reboot stop shutdown destroy}.each do |cmd|
14
+ @runner.commands.should have_key(cmd)
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,13 @@
1
+ $:.unshift File.dirname(__FILE__) + '/../lib'
2
+
3
+ require 'vmaster'
4
+
5
+ program :name, "virtualmaster"
6
+ program :version, VirtualMaster::VERSION
7
+ program :description, "VirtualMaster command line interface"
8
+ program :help_formatter, :compact
9
+
10
+ default_command :test
11
+
12
+ command :test do |c|
13
+ end
@@ -0,0 +1,31 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "vmaster/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "virtualmaster"
7
+ s.version = VirtualMaster::VERSION
8
+ s.authors = ["Radim Marek"]
9
+ s.email = ["radim@laststation.net"]
10
+ s.homepage = "https://github.com/virtualmaster/virtualmaster-cli"
11
+ s.summary = %q{Command line interface to VirtualMaster}
12
+ s.description = %q{Command line interface to VirtualMaster. Control your virtual infrastructure.}
13
+
14
+ s.rubyforge_project = "vmaster"
15
+
16
+ s.files = `git ls-files`.split("\n")
17
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
18
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
19
+ s.require_paths = ["lib"]
20
+
21
+ # specify any dependencies here; for example:
22
+ # s.add_development_dependency "rspec"
23
+ # s.add_runtime_dependency "rest-client"
24
+
25
+ s.add_dependency "commander", "~> 4.1.2"
26
+ s.add_dependency "deltacloud-client", "~> 0.5.0"
27
+ s.add_dependency "terminal-table", "~> 1.4.4"
28
+ s.add_dependency "net-ssh", "~> 2.3.0"
29
+
30
+ s.add_development_dependency "rspec", "~> 2"
31
+ end
metadata ADDED
@@ -0,0 +1,117 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: virtualmaster
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.3
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Radim Marek
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-03-13 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: commander
16
+ requirement: &70220237000360 !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ~>
20
+ - !ruby/object:Gem::Version
21
+ version: 4.1.2
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: *70220237000360
25
+ - !ruby/object:Gem::Dependency
26
+ name: deltacloud-client
27
+ requirement: &70220236995660 !ruby/object:Gem::Requirement
28
+ none: false
29
+ requirements:
30
+ - - ~>
31
+ - !ruby/object:Gem::Version
32
+ version: 0.5.0
33
+ type: :runtime
34
+ prerelease: false
35
+ version_requirements: *70220236995660
36
+ - !ruby/object:Gem::Dependency
37
+ name: terminal-table
38
+ requirement: &70220236993960 !ruby/object:Gem::Requirement
39
+ none: false
40
+ requirements:
41
+ - - ~>
42
+ - !ruby/object:Gem::Version
43
+ version: 1.4.4
44
+ type: :runtime
45
+ prerelease: false
46
+ version_requirements: *70220236993960
47
+ - !ruby/object:Gem::Dependency
48
+ name: net-ssh
49
+ requirement: &70220236992760 !ruby/object:Gem::Requirement
50
+ none: false
51
+ requirements:
52
+ - - ~>
53
+ - !ruby/object:Gem::Version
54
+ version: 2.3.0
55
+ type: :runtime
56
+ prerelease: false
57
+ version_requirements: *70220236992760
58
+ - !ruby/object:Gem::Dependency
59
+ name: rspec
60
+ requirement: &70220236991620 !ruby/object:Gem::Requirement
61
+ none: false
62
+ requirements:
63
+ - - ~>
64
+ - !ruby/object:Gem::Version
65
+ version: '2'
66
+ type: :development
67
+ prerelease: false
68
+ version_requirements: *70220236991620
69
+ description: Command line interface to VirtualMaster. Control your virtual infrastructure.
70
+ email:
71
+ - radim@laststation.net
72
+ executables:
73
+ - virtualmaster
74
+ extensions: []
75
+ extra_rdoc_files: []
76
+ files:
77
+ - .gitignore
78
+ - Gemfile
79
+ - README.mdown
80
+ - Rakefile
81
+ - bin/virtualmaster
82
+ - lib/vmaster.rb
83
+ - lib/vmaster/cli.rb
84
+ - lib/vmaster/config_command.rb
85
+ - lib/vmaster/helpers.rb
86
+ - lib/vmaster/server_commands.rb
87
+ - lib/vmaster/version.rb
88
+ - spec/commands_spec.rb
89
+ - spec/spec_helper.rb
90
+ - virtualmaster.gemspec
91
+ homepage: https://github.com/virtualmaster/virtualmaster-cli
92
+ licenses: []
93
+ post_install_message:
94
+ rdoc_options: []
95
+ require_paths:
96
+ - lib
97
+ required_ruby_version: !ruby/object:Gem::Requirement
98
+ none: false
99
+ requirements:
100
+ - - ! '>='
101
+ - !ruby/object:Gem::Version
102
+ version: '0'
103
+ required_rubygems_version: !ruby/object:Gem::Requirement
104
+ none: false
105
+ requirements:
106
+ - - ! '>='
107
+ - !ruby/object:Gem::Version
108
+ version: '0'
109
+ requirements: []
110
+ rubyforge_project: vmaster
111
+ rubygems_version: 1.8.11
112
+ signing_key:
113
+ specification_version: 3
114
+ summary: Command line interface to VirtualMaster
115
+ test_files:
116
+ - spec/commands_spec.rb
117
+ - spec/spec_helper.rb