vmfloaty 0.7.9 → 0.8.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 4c7884840968e8735caff3146f276a2f9075cf79
4
- data.tar.gz: 172f0dc34672fb184d62bb4dc7eb93d306e9d413
3
+ metadata.gz: 17d69277c7eb6a7e91324b79897833147913c900
4
+ data.tar.gz: af3ff211ec20d651442e2277cf12b42605e6b75f
5
5
  SHA512:
6
- metadata.gz: ebfe08176a0505aa253849222afc98cf5b08361db264cee13fce6947b292434f1c466e23291fdee722b3b0cf658ac50335d61f78397190e3b489c237cab10806
7
- data.tar.gz: 0d776b18978fb5fe8a1c893e1f2c1198fd78c7190561388eb7b1be334d79923ae0a39fa498e3007e7bc3c09834c05145cac5790195fa39c6c4e19d6f719254ae
6
+ metadata.gz: f14e92275d92c7aaca9d71d17a580588490e195adc4e192ab15f5d8149096d0fbab161adb63be404002beb2ca3a345fee99e9646d8e127fa9522da86fa2a344c
7
+ data.tar.gz: 48f146c619671dfc7f60e88e73c23f626e6a6798de1480c9209e05045830c02e4a4a3896a1164daa5f6e494883b1da73181ca1ab82c84a3b51b1bfbb6e9316d4
data/README.md CHANGED
@@ -7,6 +7,8 @@ A CLI helper tool for [Puppet Labs vmpooler](https://github.com/puppetlabs/vmpoo
7
7
 
8
8
  <img src="http://i.imgur.com/xGcGwuH.jpg" width=200 height=200>
9
9
 
10
+ This project is still supported by @briancain and @demophoon. Ping either of us if you'd like something merged and released.
11
+
10
12
  ## Install
11
13
 
12
14
  Grab the latest from ruby gems...
@@ -66,8 +68,10 @@ floaty get centos-7-x86_64=2 debian-7-x86_64 windows-10=3 --token mytokenstring
66
68
 
67
69
  If you do not wish to continuely specify various config options with the cli, you can have a dotfile in your home directory for some defaults. For example:
68
70
 
71
+ #### Basic configuration
72
+
69
73
  ```yaml
70
- #file at /Users/me/.vmfloaty.yml
74
+ # file at /Users/me/.vmfloaty.yml
71
75
  url: 'https://vmpooler.mycompany.net/api/v1'
72
76
  user: 'brian'
73
77
  token: 'tokenstring'
@@ -75,6 +79,66 @@ token: 'tokenstring'
75
79
 
76
80
  Now vmfloaty will use those config files if no flag was specified.
77
81
 
82
+ #### Configuring multiple services
83
+
84
+ Most commands allow you to specify a `--service <servicename>` option to allow the use of multiple vmpooler instances. This can be useful when you'd rather not specify a `--url` or `--token` by hand for alternate services.
85
+
86
+ To configure multiple services, you can set up your `~/.vmfloaty.yml` config file like this:
87
+
88
+ ```yaml
89
+ # file at /Users/me/.vmfloaty.yml
90
+ user: 'brian'
91
+ services:
92
+ main:
93
+ url: 'https://vmpooler.mycompany.net/api/v1'
94
+ token: 'tokenstring'
95
+ alternate:
96
+ url: 'https://vmpooler.alternate.net/api/v1'
97
+ token: 'alternate-tokenstring'
98
+ ```
99
+
100
+ - If you run `floaty` without a `--service <name>` option, vmfloaty will use the first configured service by default.
101
+ With the config file above, the default would be to use the 'main' vmpooler instance.
102
+ - If keys are missing for a configured service, vmfloaty will attempt to fall back to the top-level values.
103
+ With the config file above, 'brian' will be used as the username for both configured services, since neither specifies a username.
104
+
105
+ Examples using the above configuration:
106
+
107
+ List available vm types from our main vmpooler instance:
108
+ ```sh
109
+ floaty list --service main
110
+ # or, since the first configured service is used by default:
111
+ floaty list
112
+ ```
113
+
114
+ List available vm types from our alternate vmpooler instance:
115
+ ```sh
116
+ floaty list --service alternate
117
+ ```
118
+
119
+ #### Using a Nonstandard Pooler service
120
+
121
+ vmfloaty is capable of working with Puppet's [nonstandard pooler](https://github.com/puppetlabs/nspooler) in addition to the default vmpooler API. To add a nonstandard pooler service, specify an API `type` value in your service configuration, like this:
122
+
123
+ ```yaml
124
+ # file at /Users/me/.vmfloaty.yml
125
+ user: 'brian'
126
+ services:
127
+ vm:
128
+ url: 'https://vmpooler.mycompany.net/api/v1'
129
+ token: 'tokenstring'
130
+ ns:
131
+ url: 'https://nspooler.mycompany.net/api/v1'
132
+ token: 'nspooler-tokenstring'
133
+ type: 'nonstandard' # <-- 'type' is necessary for any non-vmpooler service
134
+ ```
135
+
136
+ With this configuration, you could list available OS types from nspooler like this:
137
+
138
+ ```sh
139
+ floaty list --service ns
140
+ ```
141
+
78
142
  #### Valid config keys
79
143
 
80
144
  Here are the keys that vmfloaty currently supports:
@@ -87,6 +151,8 @@ Here are the keys that vmfloaty currently supports:
87
151
  + String
88
152
  - url
89
153
  + String
154
+ - services
155
+ + Map
90
156
 
91
157
  ### Tab Completion
92
158
 
@@ -15,3 +15,9 @@ class MissingParamError < StandardError
15
15
  super
16
16
  end
17
17
  end
18
+
19
+ class ModifyError < StandardError
20
+ def initialize(msg="Could not modify VM")
21
+ super
22
+ end
23
+ end
@@ -0,0 +1,135 @@
1
+ require 'vmfloaty/errors'
2
+ require 'vmfloaty/http'
3
+ require 'faraday'
4
+ require 'json'
5
+
6
+ class NonstandardPooler
7
+ def self.list(verbose, url, os_filter = nil)
8
+ conn = Http.get_conn(verbose, url)
9
+
10
+ response = conn.get 'status'
11
+ response_body = JSON.parse(response.body)
12
+ os_list = response_body.keys.sort
13
+ os_list.delete 'ok'
14
+
15
+ os_filter ? os_list.select { |i| i[/#{os_filter}/] } : os_list
16
+ end
17
+
18
+ def self.list_active(verbose, url, token)
19
+ status = Auth.token_status(verbose, url, token)
20
+ status['reserved_hosts'] || []
21
+ end
22
+
23
+ def self.retrieve(verbose, os_type, token, url)
24
+ conn = Http.get_conn(verbose, url)
25
+ conn.headers['X-AUTH-TOKEN'] = token if token
26
+
27
+ os_string = ''
28
+ os_type.each do |os, num|
29
+ num.times do |_i|
30
+ os_string << os + '+'
31
+ end
32
+ end
33
+
34
+ os_string = os_string.chomp('+')
35
+
36
+ if os_string.empty?
37
+ raise MissingParamError, 'No operating systems provided to obtain.'
38
+ end
39
+
40
+ response = conn.post "host/#{os_string}"
41
+
42
+ res_body = JSON.parse(response.body)
43
+
44
+ if res_body['ok']
45
+ res_body
46
+ elsif response.status == 401
47
+ raise AuthError, "HTTP #{response.status}: The token provided could not authenticate to the pooler.\n#{res_body}"
48
+ else
49
+ raise "HTTP #{response.status}: Failed to obtain VMs from the pooler at #{url}/host/#{os_string}. #{res_body}"
50
+ end
51
+ end
52
+
53
+ def self.modify(verbose, url, hostname, token, modify_hash)
54
+ if token.nil?
55
+ raise TokenError, 'Token provided was nil; Request cannot be made to modify VM'
56
+ end
57
+
58
+ modify_hash.each do |key, value|
59
+ unless [:reason, :reserved_for_reason].include? key
60
+ raise ModifyError, "Configured service type does not support modification of #{key}"
61
+ end
62
+ end
63
+
64
+ if modify_hash[:reason]
65
+ # "reason" is easier to type than "reserved_for_reason", but nspooler needs the latter
66
+ modify_hash[:reserved_for_reason] = modify_hash.delete :reason
67
+ end
68
+
69
+ conn = Http.get_conn(verbose, url)
70
+ conn.headers['X-AUTH-TOKEN'] = token
71
+
72
+ response = conn.put do |req|
73
+ req.url "host/#{hostname}"
74
+ req.body = modify_hash.to_json
75
+ end
76
+
77
+ response.body.empty? ? {} : JSON.parse(response.body)
78
+ end
79
+
80
+ def self.disk(verbose, url, hostname, token, disk)
81
+ raise ModifyError, 'Configured service type does not support modification of disk space'
82
+ end
83
+
84
+ def self.snapshot(verbose, url, hostname, token)
85
+ raise ModifyError, 'Configured service type does not support snapshots'
86
+ end
87
+
88
+ def self.revert(verbose, url, hostname, token, snapshot_sha)
89
+ raise ModifyError, 'Configured service type does not support snapshots'
90
+ end
91
+
92
+ def self.delete(verbose, url, hosts, token)
93
+ if token.nil?
94
+ raise TokenError, 'Token provided was nil; Request cannot be made to delete VM'
95
+ end
96
+
97
+ conn = Http.get_conn(verbose, url)
98
+
99
+ conn.headers['X-AUTH-TOKEN'] = token if token
100
+
101
+ response_body = {}
102
+
103
+ unless hosts.is_a? Array
104
+ hosts = hosts.split(',')
105
+ end
106
+ hosts.each do |host|
107
+ response = conn.delete "host/#{host}"
108
+ res_body = JSON.parse(response.body)
109
+ response_body[host] = res_body
110
+ end
111
+
112
+ response_body
113
+ end
114
+
115
+ def self.status(verbose, url)
116
+ conn = Http.get_conn(verbose, url)
117
+
118
+ response = conn.get '/status'
119
+ JSON.parse(response.body)
120
+ end
121
+
122
+ def self.summary(verbose, url)
123
+ conn = Http.get_conn(verbose, url)
124
+
125
+ response = conn.get '/summary'
126
+ JSON.parse(response.body)
127
+ end
128
+
129
+ def self.query(verbose, url, hostname)
130
+ conn = Http.get_conn(verbose, url)
131
+
132
+ response = conn.get "host/#{hostname}"
133
+ JSON.parse(response.body)
134
+ end
135
+ end
@@ -19,6 +19,15 @@ class Pooler
19
19
  hosts
20
20
  end
21
21
 
22
+ def self.list_active(verbose, url, token)
23
+ status = Auth.token_status(verbose, url, token)
24
+ vms = []
25
+ if status[token] && status[token]['vms']
26
+ vms = status[token]['vms']['running']
27
+ end
28
+ vms
29
+ end
30
+
22
31
  def self.retrieve(verbose, os_type, token, url)
23
32
  # NOTE:
24
33
  # Developers can use `Utils.generate_os_hash` to
@@ -54,25 +63,28 @@ class Pooler
54
63
  end
55
64
  end
56
65
 
57
- def self.modify(verbose, url, hostname, token, lifetime, tags)
66
+ def self.modify(verbose, url, hostname, token, modify_hash)
58
67
  if token.nil?
59
68
  raise TokenError, "Token provided was nil. Request cannot be made to modify vm"
60
69
  end
61
70
 
62
- modify_body = {}
63
- if lifetime
64
- modify_body['lifetime'] = lifetime
65
- end
66
- if tags
67
- modify_body['tags'] = tags
71
+ modify_hash.keys.each do |key|
72
+ unless [:tags, :lifetime, :disk].include? key
73
+ raise ModifyError, "Configured service type does not support modification of #{key}."
74
+ end
68
75
  end
69
76
 
70
77
  conn = Http.get_conn(verbose, url)
71
78
  conn.headers['X-AUTH-TOKEN'] = token
72
79
 
80
+ if modify_hash['disk']
81
+ disk(verbose, url, hostname, token, modify_hash['disk'])
82
+ modify_hash.delete 'disk'
83
+ end
84
+
73
85
  response = conn.put do |req|
74
86
  req.url "vm/#{hostname}"
75
- req.body = modify_body.to_json
87
+ req.body = modify_hash.to_json
76
88
  end
77
89
 
78
90
  res_body = JSON.parse(response.body)
@@ -0,0 +1,133 @@
1
+ require 'commander/user_interaction'
2
+ require 'commander/command'
3
+ require 'vmfloaty/utils'
4
+ require 'vmfloaty/ssh'
5
+
6
+ class Service
7
+
8
+ attr_reader :config
9
+
10
+ def initialize(options, config_hash = {})
11
+ options ||= Commander::Command::Options.new
12
+ @config = Utils.get_service_config config_hash, options
13
+ @service_object = Utils.get_service_object @config['type']
14
+ end
15
+
16
+ def method_missing(m, *args, &block)
17
+ if @service_object.respond_to? m
18
+ @service_object.send(m, *args, &block)
19
+ else
20
+ super
21
+ end
22
+ end
23
+
24
+ def url
25
+ @config['url']
26
+ end
27
+
28
+ def type
29
+ @service_object.name
30
+ end
31
+
32
+ def user
33
+ unless @config['user']
34
+ puts "Enter your pooler service username:"
35
+ @config['user'] = STDIN.gets.chomp
36
+ end
37
+ @config['user']
38
+ end
39
+
40
+ def token
41
+ unless @config['token']
42
+ puts "No token found. Retrieving a token..."
43
+ @config['token'] = get_new_token(nil)
44
+ end
45
+ @config['token']
46
+ end
47
+
48
+ def get_new_token(verbose)
49
+ username = user
50
+ pass = Commander::UI::password "Enter your pooler service password:", '*'
51
+ Auth.get_token(verbose, url, username, pass)
52
+ end
53
+
54
+ def delete_token(verbose, token_value = @config['token'])
55
+ username = user
56
+ pass = Commander::UI::password "Enter your pooler service password:", '*'
57
+ Auth.delete_token(verbose, url, username, pass, token_value)
58
+ end
59
+
60
+ def token_status(verbose, token_value)
61
+ token_value ||= @config['token']
62
+ Auth.token_status(verbose, url, token_value)
63
+ end
64
+
65
+ def list(verbose, os_filter = nil)
66
+ @service_object.list verbose, url, os_filter
67
+ end
68
+
69
+ def list_active(verbose)
70
+ @service_object.list_active verbose, url, token
71
+ end
72
+
73
+ def retrieve(verbose, os_types, use_token = true)
74
+ puts 'Requesting a vm without a token...' unless use_token
75
+ token_value = use_token ? token : nil
76
+ @service_object.retrieve verbose, os_types, token_value, url
77
+ end
78
+
79
+ def ssh(verbose, host_os, use_token = true)
80
+ token_value = nil
81
+ if use_token
82
+ begin
83
+ token_value = token || get_new_token(verbose)
84
+ rescue TokenError => e
85
+ STDERR.puts e
86
+ STDERR.puts 'Could not get token... requesting vm without a token anyway...'
87
+ end
88
+ end
89
+ Ssh.ssh(verbose, host_os, token_value, url)
90
+ end
91
+
92
+ def pretty_print_running(verbose, hostnames = [])
93
+ if hostnames.empty?
94
+ puts "You have no running VMs."
95
+ else
96
+ puts "Running VMs:"
97
+ @service_object.pretty_print_hosts(verbose, hostnames, url)
98
+ end
99
+ end
100
+
101
+ def query(verbose, hostname)
102
+ @service_object.query verbose, url, hostname
103
+ end
104
+
105
+ def modify(verbose, hostname, modify_hash)
106
+ @service_object.modify verbose, url, hostname, token, modify_hash
107
+ end
108
+
109
+ def delete(verbose, hosts)
110
+ @service_object.delete verbose, url, hosts, token
111
+ end
112
+
113
+ def status(verbose)
114
+ @service_object.status verbose, url
115
+ end
116
+
117
+ def summary(verbose)
118
+ @service_object.summary verbose, url
119
+ end
120
+
121
+ def snapshot(verbose, hostname)
122
+ @service_object.snapshot verbose, url, hostname, token
123
+ end
124
+
125
+ def revert(verbose, hostname, snapshot_sha)
126
+ @service_object.revert verbose, url, hostname, token, snapshot_sha
127
+ end
128
+
129
+ def disk(verbose, hostname, disk)
130
+ @service_object.disk(verbose, url, hostname, token, disk)
131
+ end
132
+
133
+ end