cloudstack_cloner 0.0.1 → 0.0.2

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: 60dd8f4ce7db03e2c028187255f2e92868e67de6
4
- data.tar.gz: e2dbeb3fcfdd1016bebca529754f0f442cf3a9c8
3
+ metadata.gz: 416bf2f75c671c268cfd99cae1b6c2b7dae9ae2a
4
+ data.tar.gz: a4a4f82678ba62f2dea7a6ae1afc9e784825f27e
5
5
  SHA512:
6
- metadata.gz: 82a48a9715c7ca16483a81cd81afd713165c0f8c13621e811c57f10a28a78cede4d20faae6ef3b50f734962b93fb31962712106a3955becaacd50cb7386feffc
7
- data.tar.gz: 9ddcc880e8c8edc4503ab70691f289c6e5a5ceaaa880460c146c4d5a6a1b84d3a9e34f455bbfc938d4154a1f8ad4850cac8de56193075799a8585b666f3c13a7
6
+ metadata.gz: bbd4513764adb7d27d2303950818fd8aa26d44b47b9ca35ae5a02dd3da44d7736fd85924ad84520a9c9ab8fa7e20f1e5ca55949aa665bd1eb58fcebe4705c785
7
+ data.tar.gz: 647842bf094b20eec1f197ea4e4ae660b0be85439d3b244410524bbcfc629c541ed6d6d424c16bc013ead2a0bc43ebb85b918654a16cbe4a1eff38f6e5a5b47d
data/README.md CHANGED
@@ -1,30 +1,32 @@
1
1
  # CloudstackCloner
2
2
 
3
- Automated CloudStack VM cloning and copying and attaching of existing data disks.
3
+ [![Gem Version](https://badge.fury.io/rb/cloudstack_cloner.png)](http://badge.fury.io/rb/cloudstack_cloner)
4
+
5
+ Automated CloudStack VM cloning and copying/attaching of existing data disks.
6
+ CloudstackCloner uses [cloudstack_client](https://github.com/niwo/cloudstack_client) for CloudStack API communication.
4
7
 
5
8
  ## Installation
6
9
 
7
- Add this line to your application's Gemfile:
10
+ Install the Gem:
8
11
 
9
- ```ruby
10
- gem 'cloudstack_cloner'
12
+ ```bash
13
+ $ gem install cloudstack_cloner
11
14
  ```
12
15
 
13
- And then execute:
14
-
15
- $ bundle
16
-
17
- Or install it yourself as:
16
+ ## Configuration
18
17
 
19
- $ gem install cloudstack_cloner
18
+ A [cloudstack-cli](https://github.com/niwo/cloudstack-cli) style configuration file is used for setting up URL, keys and secrets of your CloudStack API connection.
20
19
 
21
20
  ## Usage
22
21
 
22
+ ### Preconditions
23
+ * The machine to be cloned has to be in "Stopped" state.
24
+ * The data disks to be copied have to be in state "Ready".
23
25
 
24
26
  ### Example
25
27
 
26
28
  ```bash
27
- cloudstack_cloner clone --virtual_machine test01 --clone-name test-clone --project Playground --data-volumes test -e prod --offering 2cpu_2gb
29
+ $ cloudstack_cloner clone --virtual_machine test01 --clone-name test-clone --project Playground --data-volumes test-volume --offering 2cpu_2gb
28
30
  ```
29
31
 
30
32
  ## Contributing
@@ -24,21 +24,20 @@ module CloudstackCloner
24
24
 
25
25
  desc "clone", "Clone a virtual machine"
26
26
  option :virtual_machine,
27
- desc: "name of the vm to clone",
27
+ desc: "Name of the vm to clone",
28
28
  required: true
29
29
  option :project,
30
- desc: "name of project"
30
+ desc: "Name of project"
31
31
  option :clone_name,
32
- desc: "name of the new vm",
32
+ desc: "Name of the new vm",
33
33
  required: true
34
34
  option :offering,
35
- desc: "name of the compute offering for the new vm"
35
+ desc: "Name of the compute offering for the new vm"
36
36
  option :data_volumes,
37
- desc: "names of data volumes to attach",
37
+ desc: "Names of data volumes to attach, separated by space",
38
38
  type: :array
39
39
  def clone
40
- opts = options.dup
41
- opts = resolve_project(opts)
40
+ opts = resolve_project(options.dup)
42
41
  opts = resolve_virtual_machine(opts)
43
42
  opts = resolve_compute_offering(opts)
44
43
  clone_vm(opts)
@@ -9,8 +9,16 @@ module CloudstackCloner
9
9
  opts.merge(listall: true, name: opts[:virtual_machine])
10
10
  ).first
11
11
 
12
+ if client.list_virtual_machines(
13
+ opts.merge(listall: true, name: opts[:clone_name])
14
+ ).size > 0
15
+ say_log "Failure: ", :red
16
+ say "VM with name #{opts[:clone_name]} already exists."
17
+ exit 1
18
+ end
19
+
12
20
  if vm["state"] == "Running"
13
- say "Failure: ", :red
21
+ say_log "Failure: ", :red
14
22
  say "VM #{vm["name"]} has to be stopped in order to create a template."
15
23
  exit 1
16
24
  end
@@ -22,7 +30,7 @@ module CloudstackCloner
22
30
  type: "DATADISK",
23
31
  project_id: opts[:project_id]
24
32
  ).first
25
- say "Failure: ", :red
33
+ say_log "Failure: ", :red
26
34
  say "Volume #{disk} not found."
27
35
  exit 1
28
36
  end
@@ -30,7 +38,6 @@ module CloudstackCloner
30
38
  end
31
39
 
32
40
  volume = client.list_volumes(opts.merge(listall: true, type: "root")).first
33
-
34
41
  templ_name = "#{vm["name"]}-#{Time.now.strftime("%F")}"
35
42
 
36
43
  if template = client.list_templates(
@@ -39,9 +46,9 @@ module CloudstackCloner
39
46
  projectid: opts[:project_id],
40
47
  templatefilter: "self"
41
48
  ).first
42
- say "Template #{templ_name} already exists.", :green
49
+ say_log "Template #{templ_name} already exists.", :green
43
50
  else
44
- say "Create template from volume #{volume["name"]} ", :yellow
51
+ say_log "Create template from volume #{volume["name"]} ", :yellow
45
52
  template = client.create_template(
46
53
  name: templ_name,
47
54
  displaytext: templ_name,
@@ -51,7 +58,7 @@ module CloudstackCloner
51
58
  say " [OK]", :green
52
59
  end
53
60
 
54
- say "Creating VM from template #{template["name"]} ", :yellow
61
+ say_log "Creating VM from template #{template["name"]} ", :yellow
55
62
  clone = client.deploy_virtual_machine(
56
63
  name: opts[:clone_name],
57
64
  displaytext: opts[:clone_name],
@@ -65,11 +72,11 @@ module CloudstackCloner
65
72
 
66
73
 
67
74
  data_volumes.each do |volume|
68
- say "Creating snapshot for volume #{volume["name"]} ", :yellow
75
+ say_log "Creating snapshot for volume #{volume["name"]} ", :yellow
69
76
  snapshot = client.create_snapshot(volumeid: volume["id"])["snapshot"]
70
77
  say " [OK]", :green
71
78
 
72
- say "Creating clone of volume #{volume["name"]} ", :yellow
79
+ say_log "Creating clone of volume #{volume["name"]} ", :yellow
73
80
  volume = client.create_volume(
74
81
  name: "#{volume["name"]}_#{opts[:clone_name]}",
75
82
  snapshot_id: snapshot["id"],
@@ -77,14 +84,14 @@ module CloudstackCloner
77
84
  )["volume"]
78
85
  say " [OK]", :green
79
86
 
80
- say "Attach clone of volume #{volume["name"]} to VM #{clone["name"]} ", :yellow
87
+ say_log "Attach clone of volume #{volume["name"]} to VM #{clone["name"]} ", :yellow
81
88
  client.attach_volume(
82
89
  id: volume["id"],
83
90
  virtualmachineid: clone["id"]
84
91
  )
85
92
  say " [OK]", :green
86
93
 
87
- say "Delete snapshot of volume #{volume["name"]} ", :yellow
94
+ say_log "Delete snapshot of volume #{volume["name"]} ", :yellow
88
95
  volume = client.delete_snapshot(id: snapshot["id"])
89
96
  say " [OK]", :green
90
97
  end
@@ -93,6 +100,11 @@ module CloudstackCloner
93
100
 
94
101
  private
95
102
 
103
+ def say_log(message, color = nil)
104
+ say "[#{Time.new.strftime("%F-%X")}] - "
105
+ say "#{message}", color
106
+ end
107
+
96
108
  def client
97
109
  @config ||= load_configuration(options[:config_file], options[:env]).first
98
110
  @client ||= CloudstackClient::Client.new(
@@ -1,81 +1,67 @@
1
1
  module CloudstackCloner
2
2
  module OptionResolver
3
3
 
4
- def vm_options_to_params(options = options)
5
- resolve_zone(options)
6
- resolve_project(options)
7
- resolve_compute_offering(options)
8
- resolve_template(options)
9
- resolve_disk_offering(options)
10
- resolve_iso(options)
11
- unless options[:template_id]
12
- say "Error: Template or ISO is required.", :red
13
- exit 1
14
- end
15
- resolve_networks(options)
16
- end
17
-
18
- def resolve_zone(options = options)
19
- if options[:zone]
4
+ def resolve_zone(opts)
5
+ if opts[:zone]
20
6
  zones = client.list_zones
21
- zone = zones.find {|z| z['name'] == options[:zone] }
7
+ zone = zones.find {|z| z['name'] == opts[:zone] }
22
8
  if !zone
23
- msg = options[:zone] ? "Zone '#{options[:zone]}' is invalid." : "No zone found."
9
+ msg = opts[:zone] ? "Zone '#{opts[:zone]}' is invalid." : "No zone found."
24
10
  say "Error: #{msg}", :red
25
11
  exit 1
26
12
  end
27
- options[:zone_id] = zone['id']
13
+ opts[:zone_id] = zone['id']
28
14
  end
29
- options
15
+ opts
30
16
  end
31
17
 
32
- def resolve_domain(options = options)
33
- if options[:domain]
34
- if domain = client.list_domains(name: options[:domain]).first
35
- options[:domain_id] = domain['id']
18
+ def resolve_domain(opts)
19
+ if opts[:domain]
20
+ if domain = client.list_domains(name: opts[:domain]).first
21
+ opts[:domain_id] = domain['id']
36
22
  else
37
- say "Error: Domain #{options[:domain]} not found.", :red
23
+ say "Error: Domain #{opts[:domain]} not found.", :red
38
24
  exit 1
39
25
  end
40
26
  end
41
- options
27
+ opts
42
28
  end
43
29
 
44
- def resolve_project(options = options)
45
- if options[:project]
46
- if %w(ALL -1).include? options[:project]
47
- options[:project_id] = "-1"
48
- elsif project = client.list_projects(name: options[:project], listall: true).first
49
- options[:project_id] = project['id']
30
+ def resolve_project(opts)
31
+ if opts[:project]
32
+ if %w(ALL -1).include? opts[:project]
33
+ opts[:project_id] = "-1"
34
+ elsif project = client.list_projects(name: opts[:project], listall: true).first
35
+ opts[:project_id] = project['id']
50
36
  else
51
- say "Error: Project #{options[:project]} not found.", :red
37
+ say "Error: Project #{opts[:project]} not found.", :red
52
38
  exit 1
53
39
  end
54
40
  end
55
- options
41
+ opts
56
42
  end
57
43
 
58
- def resolve_account(options = options)
59
- if options[:account]
60
- if account = client.list_accounts(name: options[:account], listall: true).first
61
- options[:account_id] = account['id']
62
- options[:domain_id] = account['domainid']
44
+ def resolve_account(opts)
45
+ if opts[:account]
46
+ if account = client.list_accounts(name: opts[:account], listall: true).first
47
+ opts[:account_id] = account['id']
48
+ opts[:domain_id] = account['domainid']
63
49
  else
64
- say "Error: Account #{options[:account]} not found.", :red
50
+ say "Error: Account #{opts[:account]} not found.", :red
65
51
  exit 1
66
52
  end
67
53
  end
68
- options
54
+ opts
69
55
  end
70
56
 
71
- def resolve_networks(options = options)
57
+ def resolve_networks(opts)
72
58
  networks = []
73
59
  available_networks = network = client.list_networks(
74
- zone_id: options[:zone_id],
75
- project_id: options[:project_id]
60
+ zone_id: opts[:zone_id],
61
+ project_id: opts[:project_id]
76
62
  )
77
- if options[:networks]
78
- options[:networks].each do |name|
63
+ if opts[:networks]
64
+ opts[:networks].each do |name|
79
65
  unless network = available_networks.find { |n| n['name'] == name }
80
66
  say "Error: Network '#{name}' not found.", :red
81
67
  exit 1
@@ -85,100 +71,81 @@ module CloudstackCloner
85
71
  end
86
72
  networks.compact!
87
73
  if networks.empty?
88
- #unless default_network = client.list_networks(project_id: options[:project_id]).find {
74
+ #unless default_network = client.list_networks(project_id: opts[:project_id]).find {
89
75
  # |n| n['isdefault'] == true }
90
- unless default_network = client.list_networks(project_id: options[:project_id]).first
76
+ unless default_network = client.list_networks(project_id: opts[:project_id]).first
91
77
  say "Error: No default network found.", :red
92
78
  exit 1
93
79
  end
94
80
  networks << available_networks.first['id'] rescue nil
95
81
  end
96
- options[:network_ids] = networks.join(',')
97
- options
98
- end
99
-
100
- def resolve_iso(options = options)
101
- if options[:iso]
102
- unless iso = client.list_isos(
103
- name: options[:iso],
104
- project_id: options[:project_id]
105
- ).first
106
- say "Error: Iso '#{options[:iso]}' is invalid.", :red
107
- exit 1
108
- end
109
- unless options[:diskoffering_id]
110
- say "Error: a disk offering is required when using iso.", :red
111
- exit 1
112
- end
113
- options[:template_id] = iso['id']
114
- options['hypervisor'] = (options[:hypervisor] || 'vmware')
115
- end
116
- options
82
+ opts[:network_ids] = networks.join(',')
83
+ opts
117
84
  end
118
85
 
119
- def resolve_template(options = options)
120
- if options[:template]
86
+ def resolve_template(opts)
87
+ if opts[:template]
121
88
  if template = client.list_templates(
122
- name: options[:template],
89
+ name: opts[:template],
123
90
  template_filter: "executable",
124
- project_id: options[:project_id]
91
+ project_id: opts[:project_id]
125
92
  ).first
126
- options[:template_id] = template['id']
93
+ opts[:template_id] = template['id']
127
94
  else
128
- say "Error: Template #{options[:template]} not found.", :red
95
+ say "Error: Template #{opts[:template]} not found.", :red
129
96
  exit 1
130
97
  end
131
98
  end
132
- options
99
+ opts
133
100
  end
134
101
 
135
- def resolve_compute_offering(options = options)
136
- if options[:offering]
137
- if offering = client.list_service_offerings(name: options[:offering]).first
138
- options[:service_offering_id] = offering['id']
102
+ def resolve_compute_offering(opts)
103
+ if opts[:offering]
104
+ if offering = client.list_service_offerings(name: opts[:offering]).first
105
+ opts[:service_offering_id] = offering['id']
139
106
  else
140
- say "Error: Offering #{options[:offering]} not found.", :red
107
+ say "Error: Offering #{opts[:offering]} not found.", :red
141
108
  exit 1
142
109
  end
143
110
  end
144
- options
111
+ opts
145
112
  end
146
113
 
147
- def resolve_disk_offering(options = options)
148
- if options[:disk_offering]
149
- unless disk_offering = client.list_disk_offerings(name: options[:disk_offering]).first
150
- say "Error: Disk offering '#{options[:disk_offering]}' not found.", :red
114
+ def resolve_disk_offering(opts)
115
+ if opts[:disk_offering]
116
+ unless disk_offering = client.list_disk_offerings(name: opts[:disk_offering]).first
117
+ say "Error: Disk offering '#{opts[:disk_offering]}' not found.", :red
151
118
  exit 1
152
119
  end
153
- options[:disk_offering_id] = disk_offering['id']
120
+ opts[:disk_offering_id] = disk_offering['id']
154
121
  end
155
- options
122
+ opts
156
123
  end
157
124
 
158
- def resolve_virtual_machine(options = options.dup)
159
- if options[:virtual_machine]
160
- args = { name: options[:virtual_machine], listall: true }
161
- args[:project_id] = options[:project_id]
125
+ def resolve_virtual_machine(opts)
126
+ if opts[:virtual_machine]
127
+ args = { name: opts[:virtual_machine], listall: true }
128
+ args[:project_id] = opts[:project_id]
162
129
  unless vm = client.list_virtual_machines(args).first
163
- say "Error: VM '#{options[:virtual_machine]}' not found.", :red
130
+ say "Error: VM '#{opts[:virtual_machine]}' not found.", :red
164
131
  exit 1
165
132
  end
166
- options[:virtual_machine_id] = vm['id']
133
+ opts[:virtual_machine_id] = vm['id']
167
134
  end
168
- options
135
+ opts
169
136
  end
170
137
 
171
- def resolve_snapshot(options = options)
172
- if options[:snapshot]
173
- args = { name: options[:snapshot], listall: true }
174
- args[:project_id] = options[:project_id]
138
+ def resolve_snapshot(opts)
139
+ if opts[:snapshot]
140
+ args = { name: opts[:snapshot], listall: true }
141
+ args[:project_id] = opts[:project_id]
175
142
  unless snapshot = client.list_snapshots(args).first
176
- say "Error: Snapshot '#{options[:snapshot]}' not found.", :red
143
+ say "Error: Snapshot '#{opts[:snapshot]}' not found.", :red
177
144
  exit 1
178
145
  end
179
- options[:snapshot_id] = snapshot['id']
146
+ opts[:snapshot_id] = snapshot['id']
180
147
  end
181
- options
148
+ opts
182
149
  end
183
150
 
184
151
  end
@@ -1,3 +1,3 @@
1
1
  module CloudstackCloner
2
- VERSION = "0.0.1"
2
+ VERSION = "0.0.2"
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: cloudstack_cloner
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.0.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - niwo
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-07-16 00:00:00.000000000 Z
11
+ date: 2015-07-20 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -106,7 +106,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
106
106
  version: '0'
107
107
  requirements: []
108
108
  rubyforge_project:
109
- rubygems_version: 2.2.2
109
+ rubygems_version: 2.4.5
110
110
  signing_key:
111
111
  specification_version: 4
112
112
  summary: Automated CloudStack VM cloning