vmfloaty 0.7.9 → 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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