dopv 0.11.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +23 -0
- data/.rspec +2 -0
- data/ChangeLog.md +456 -0
- data/Gemfile +6 -0
- data/Gemfile.lock +260 -0
- data/Guardfile +22 -0
- data/LICENSE.txt +177 -0
- data/README.md +214 -0
- data/Rakefile +6 -0
- data/bin/dopv +4 -0
- data/dopv.gemspec +52 -0
- data/lib/dopv.rb +166 -0
- data/lib/dopv/cli.rb +54 -0
- data/lib/dopv/cli/command_add.rb +37 -0
- data/lib/dopv/cli/command_export.rb +26 -0
- data/lib/dopv/cli/command_import.rb +32 -0
- data/lib/dopv/cli/command_list.rb +18 -0
- data/lib/dopv/cli/command_remove.rb +29 -0
- data/lib/dopv/cli/command_run.rb +38 -0
- data/lib/dopv/cli/command_update.rb +35 -0
- data/lib/dopv/cli/command_validate.rb +30 -0
- data/lib/dopv/infrastructure.rb +40 -0
- data/lib/dopv/infrastructure/providers/baremetal.rb +12 -0
- data/lib/dopv/infrastructure/providers/base.rb +422 -0
- data/lib/dopv/infrastructure/providers/openstack.rb +308 -0
- data/lib/dopv/infrastructure/providers/ovirt.rb +228 -0
- data/lib/dopv/infrastructure/providers/vsphere.rb +322 -0
- data/lib/dopv/log.rb +14 -0
- data/lib/dopv/persistent_disk.rb +128 -0
- data/lib/dopv/plan.rb +17 -0
- data/lib/dopv/state_store.rb +87 -0
- data/lib/dopv/version.rb +3 -0
- data/spec/data/hooks/test_hook_script_1 +9 -0
- data/spec/data/hooks/test_hook_script_2 +10 -0
- data/spec/data/plans/test-plan-1.yaml +140 -0
- data/spec/spec_helper.rb +112 -0
- data/spec/unit/dopv/dopv_spec.rb +7 -0
- data/spec/unit/dopv/persistent_disk_spec.rb +38 -0
- data/spec/unit/dopv/plan_spec.rb +34 -0
- data/spec/unit/dopv/version_spec.rb +17 -0
- metadata +401 -0
data/README.md
ADDED
@@ -0,0 +1,214 @@
|
|
1
|
+
# Dopv
|
2
|
+
|
3
|
+
Dopv orchestrates deployments of nodes. A node can be a virtual machine or a bare-metal compute node.
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
Add this line to your application's Gemfile:
|
8
|
+
|
9
|
+
gem 'dopv'
|
10
|
+
|
11
|
+
And then execute:
|
12
|
+
|
13
|
+
$ bundle
|
14
|
+
|
15
|
+
Or install it yourself as:
|
16
|
+
|
17
|
+
$ gem install dopv
|
18
|
+
|
19
|
+
## Usage
|
20
|
+
|
21
|
+
### Library
|
22
|
+
Deploy a plan
|
23
|
+
```ruby
|
24
|
+
require 'dopv'
|
25
|
+
plan = ::Dopv::load_plan(plan_file)
|
26
|
+
data_volumes_db = ::Dopv::load_data_volumes_db(db_file)
|
27
|
+
::Dopv::run_plan(plan, data_volumes_db)
|
28
|
+
```
|
29
|
+
Undeploy a plan while keeping data volumes
|
30
|
+
```ruby
|
31
|
+
require 'dopv'
|
32
|
+
plan = ::Dopv::load_plan(plan_file)
|
33
|
+
data_volumes_db = ::Dopv::load_data_volumes_db(db_file)
|
34
|
+
::Dopv::run_plan(plan, data_volumes_db, :undeploy)
|
35
|
+
```
|
36
|
+
Undeploy a plan and remove data volumes from infrastructure provider (stack) as
|
37
|
+
well as from persistent data volumes DB:
|
38
|
+
```ruby
|
39
|
+
require 'dopv'
|
40
|
+
plan = ::Dopv::load_plan(plan_file)
|
41
|
+
data_volumes_db = ::Dopv::load_data_volumes_db(db_file)
|
42
|
+
::Dopv::run_plan(plan, data_volumes_db, :undeploy, true)
|
43
|
+
```
|
44
|
+
|
45
|
+
### CLI
|
46
|
+
A command line interface utility `dopv` is provided.
|
47
|
+
|
48
|
+
#### Getting help
|
49
|
+
A help can be obtained by calling `dopv --help`:
|
50
|
+
```
|
51
|
+
$ dopv --help
|
52
|
+
NAME
|
53
|
+
dopv - DOPv command line tool
|
54
|
+
|
55
|
+
SYNOPSIS
|
56
|
+
dopv [global options] command [command options] [arguments...]
|
57
|
+
|
58
|
+
VERSION
|
59
|
+
0.7.0
|
60
|
+
|
61
|
+
GLOBAL OPTIONS
|
62
|
+
--help - Show this message
|
63
|
+
--logfile, -l path_to_log_file - Log file (default: none)
|
64
|
+
--[no-]trace, -t - Show stacktrace on crash
|
65
|
+
--verbosity, -v level - Verbosity of the command line tool (default: info)
|
66
|
+
--version - Display the program version
|
67
|
+
|
68
|
+
COMMANDS
|
69
|
+
add - Add a new plan file to the plan cache
|
70
|
+
deploy - Deploy a plan
|
71
|
+
export - Export the internal disk state into a local diskdb file
|
72
|
+
help - Shows a list of commands or help for one command
|
73
|
+
import - Import a diskdb file into the internal state store
|
74
|
+
list - Show the list of plans in the plan store
|
75
|
+
remove - Remove an existing plan from the plan cache
|
76
|
+
undeploy - Undeploy a plan
|
77
|
+
update - Update the plan and/or the plan state for a given plan yaml or plan name.
|
78
|
+
validate - Validate plan file
|
79
|
+
```
|
80
|
+
|
81
|
+
#### Validating a plan
|
82
|
+
The deployment plan `/tmp/foo_plan.yml` could be validated by issuing:
|
83
|
+
```
|
84
|
+
$ dopv validate -p /tmp/foo_plan.yml
|
85
|
+
Plan valid.
|
86
|
+
```
|
87
|
+
|
88
|
+
Please note that the plan is always validated before any action takes place.
|
89
|
+
|
90
|
+
#### Adding a plan
|
91
|
+
In order to add a plan to plan cache, use `dopv` `add` command:
|
92
|
+
```
|
93
|
+
$ dopv add /tmp/bar_plan.yml
|
94
|
+
```
|
95
|
+
|
96
|
+
Please note that this feature is considered experimental and should be used with extreme care.
|
97
|
+
|
98
|
+
#### Updating a plan
|
99
|
+
In order to update a plan in the cache after some local changes have been introduced, use `dopv`
|
100
|
+
`update` command:
|
101
|
+
```
|
102
|
+
$ dopv update /tmp/bar_plan.yml
|
103
|
+
```
|
104
|
+
|
105
|
+
One may use `-c` or `-i` in order to remove the existing disk information and start with a clean
|
106
|
+
state or to ignore the update and to merely set it to the latest version.
|
107
|
+
|
108
|
+
Please note that this feature is considered experimental and should be used with extreme care.
|
109
|
+
|
110
|
+
#### Listing plans in cache
|
111
|
+
One may use the `list` command to check which plans are stored in the plan cache.
|
112
|
+
|
113
|
+
#### Deploying a plan
|
114
|
+
To get a help on deploy CLI options launch `dopv help deploy` argument:
|
115
|
+
```
|
116
|
+
$ dopv help deploy
|
117
|
+
NAME
|
118
|
+
deploy - Deploy a plan
|
119
|
+
|
120
|
+
SYNOPSIS
|
121
|
+
dopv [global options] deploy [command options]
|
122
|
+
|
123
|
+
COMMAND OPTIONS
|
124
|
+
--diskdb, -d path_to_db_file - Use a local diskdb file and import/export it automatically (default: none)
|
125
|
+
--exclude_nodes=node01.example.com,node02.example.com,/example\.com$/ - Exclude this nodes from the run (default: )
|
126
|
+
--exclude_nodes_by_config='{"var1": ["val1", "/val2/"], "var2": "val2"}' - Exclude nodes with this config from the run (You have to specify a JSON hash here) (default: {})
|
127
|
+
--exclude_roles=role01,role01,/^rolepattern/ - Exclude this roles from the run (default: )
|
128
|
+
--nodes=node01.example.com,node02.example.com,/example\.com$/ - Run plans for this nodes only (default: )
|
129
|
+
--nodes_by_config='{"var1": ["val1", "/val2/"], "var2": "val2"}' - Run plans for this nodes with this config only (You have to specify a JSON hash here) (default: {})
|
130
|
+
--plan, -p path_to_plan_file - plan name from the store or plan file to deploy. If a plan file is given DOPv will run in oneshot mode and add/remove
|
131
|
+
the plan automatically to the plan store (required, default: none)
|
132
|
+
--roles=role01,role01,/^rolepattern/ - Run plans for this roles only (default: )
|
133
|
+
```
|
134
|
+
To deploy a plan located at `/tmp/plan.yaml` and store and/or load persistent disks database located at `/tmp/pdisks.yaml` one can launch `dopv` with following options:
|
135
|
+
```
|
136
|
+
$ dopv deploy -p /tmp/pdisks.yaml -d /tmp/pdisks.yaml
|
137
|
+
```
|
138
|
+
|
139
|
+
To deploy only nodes matching a regular expression `^foo-[1-9]+\.bar\.baz$` that are defined in a plan called `/tmp/plan.yaml` and store and/or load persistent disks database located at `/tmp/pdisks.yaml` one can launch `dopv` with following options:
|
140
|
+
```
|
141
|
+
$ dopv deploy -p /tmp/pdisks.yaml -d /tmp/pdisks.yaml --nodes=/^foo-[1-9]+\.bar\.baz$/
|
142
|
+
```
|
143
|
+
|
144
|
+
Please note that disk database file is created if it does not exist.
|
145
|
+
|
146
|
+
#### Undeploying a plan
|
147
|
+
To get a help on undeploy CLI options launch `dopv` `help undeploy` argument:
|
148
|
+
```
|
149
|
+
$ dopv help undeploy
|
150
|
+
NAME
|
151
|
+
undeploy - Undeploy a plan
|
152
|
+
|
153
|
+
SYNOPSIS
|
154
|
+
dopv [global options] undeploy [command options]
|
155
|
+
|
156
|
+
COMMAND OPTIONS
|
157
|
+
--diskdb, -d path_to_db_file - (default: none)
|
158
|
+
--plan, -p path_to_plan_file - (required, default: none)
|
159
|
+
ME
|
160
|
+
undeploy - Undeploy a plan
|
161
|
+
|
162
|
+
SYNOPSIS
|
163
|
+
dopv [global options] undeploy [command options]
|
164
|
+
|
165
|
+
COMMAND OPTIONS
|
166
|
+
--diskdb, -d path_to_db_file - Use a local diskdb file and import/export it automatically (default: none)
|
167
|
+
--exclude_nodes=node01.example.com,node02.example.com,/example\.com$/ - Exclude this nodes from the run (default: )
|
168
|
+
--exclude_nodes_by_config='{"var1": ["val1", "/val2/"], "var2": "val2"}' - Exclude nodes with this config from the run (You have to specify a JSON hash here) (default: {})
|
169
|
+
--exclude_roles=role01,role01,/^rolepattern/ - Exclude this roles from the run (default: )
|
170
|
+
--nodes=node01.example.com,node02.example.com,/example\.com$/ - Run plans for this nodes only (default: )
|
171
|
+
--nodes_by_config='{"var1": ["val1", "/val2/"], "var2": "val2"}' - Run plans for this nodes with this config only (You have to specify a JSON hash here) (default: {})
|
172
|
+
--plan, -p path_to_plan_file - plan name from the store or plan file to undeploy. If a plan file is given DOPv will run in oneshot mode and
|
173
|
+
add/remove the plan automatically to the plan store (required, default: none)
|
174
|
+
--[no-]rmdisk, -r - Remove the disks
|
175
|
+
--roles=role01,role01,/^rolepattern/ - Run plans for this roles only (default: ) --[no-]rmdisk, -r -
|
176
|
+
```
|
177
|
+
|
178
|
+
In order to destroy a deployment specified by a plan located at `/tmp/plan.yaml` and persistent disks database located at `/tmp/pdisks.yaml` one can launch `dopv` with following options:
|
179
|
+
```
|
180
|
+
$ dopv undeploy -p /tmp/pdisks.yaml -d /tmp/pdisks.yaml
|
181
|
+
```
|
182
|
+
If you also want to remove the data volumes of each node and remove their records from persistent data volumes DB, please specify `-r` or `--rmdisk` option as shown bellow:
|
183
|
+
```
|
184
|
+
$ dopv undeploy -p /tmp/pdisks.yaml -d /tmp/pdisks.yaml -r
|
185
|
+
```
|
186
|
+
If you'd like to selectively remove a node called `foo.bar.baz` as well as nodes that match regular expression `^foo[1-5]\.bar\.baz$` and their data disks, you would use `--nodes` filter:
|
187
|
+
```
|
188
|
+
$ dopv undeploy -p /tmp/pdisks.yaml -d /tmp/pdisks.yaml -r --nodes=foo.bar.baz,/^foo[1-5]\.bar\.baz$/
|
189
|
+
```
|
190
|
+
If you'd like to remove all nodes but those matching `foo.bar.baz` as well as nodes that match regular expression `^foo[1-5]\.bar\.baz$` and their data disks, you would use `--exclude_nodes` filter:
|
191
|
+
```
|
192
|
+
$ dopv undeploy -p /tmp/pdisks.yaml -d /tmp/pdisks.yaml -r --exclude_nodes=foo.bar.baz,/^foo[1-5]\.bar\.baz$/
|
193
|
+
```
|
194
|
+
|
195
|
+
#### Logging
|
196
|
+
By default `dopv` logs messages with `INFO` level and higher to standard output. In order to log to a file `-l` can be specified. The `-v` option is used to set a different log threshold. Following is an example of logging everything (`DEBUG` and above) into `/tmp/dopv.log` during plan deployment:
|
197
|
+
```
|
198
|
+
$ dopv -l /tmp/dopv.log -v debug deploy -p /tmp/plan.yaml -d /tmp/disks.yaml
|
199
|
+
```
|
200
|
+
|
201
|
+
## Node Filtering Notes
|
202
|
+
Please note that `dopv` filtering by configuration and roles is not yet supported.
|
203
|
+
|
204
|
+
## Plan
|
205
|
+
A plan format and description can be found [here](https://github.com/swisscom/dop_common/blob/master/doc/plan_format_v0.0.1.md). A plan example can be found [here](https://github.com/swisscom/dop_common/blob/master/doc/plan_format_v0.0.1.md)
|
206
|
+
|
207
|
+
|
208
|
+
## Contributing
|
209
|
+
|
210
|
+
1. Fork it
|
211
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
212
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
213
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
214
|
+
5. Create new Pull Request
|
data/Rakefile
ADDED
data/bin/dopv
ADDED
data/dopv.gemspec
ADDED
@@ -0,0 +1,52 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'dopv/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "dopv"
|
8
|
+
spec.version = Dopv::VERSION
|
9
|
+
spec.authors = ["Pavol Dilung", "Andreas Zuber"]
|
10
|
+
spec.email = ["pavol.dilung@swisscom.com", "azuber@puzzle.ch"]
|
11
|
+
spec.description = %q{Deployment orchestrator for VMs}
|
12
|
+
spec.summary = %q{Deployment orchestrator for VMs}
|
13
|
+
spec.homepage = "https://github.com/swisscom/dopv"
|
14
|
+
spec.license = "Apache-2.0"
|
15
|
+
|
16
|
+
spec.files = `git ls-files`.split($/)
|
17
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
|
+
spec.require_paths = ["lib"]
|
20
|
+
|
21
|
+
spec.add_development_dependency "bundler", "~> 1.3"
|
22
|
+
spec.add_development_dependency "rake"
|
23
|
+
spec.add_development_dependency "rspec"
|
24
|
+
spec.add_development_dependency "rspec-mocks"
|
25
|
+
spec.add_development_dependency "rspec-command"
|
26
|
+
spec.add_development_dependency "simplecov"
|
27
|
+
spec.add_development_dependency "pry"
|
28
|
+
if RUBY_VERSION < "2"
|
29
|
+
spec.add_development_dependency "pry-debugger"
|
30
|
+
else
|
31
|
+
spec.add_development_dependency "pry-byebug"
|
32
|
+
end
|
33
|
+
# Newer guard listen versions do not work properly with RB <= 2.2.2
|
34
|
+
spec.add_development_dependency "listen", "~> 3.0.8"
|
35
|
+
spec.add_development_dependency "guard-ctags-bundler"
|
36
|
+
|
37
|
+
spec.add_dependency "json", "~> 1.8"
|
38
|
+
spec.add_dependency 'logger-colors', '~> 1'
|
39
|
+
#spec.add_dependency "rest-client", "~> 1.7"
|
40
|
+
spec.add_dependency "rbovirt", "~> 0.1", '>= 0.1.3'
|
41
|
+
spec.add_dependency "rbvmomi", "~> 1.8.2"
|
42
|
+
spec.add_dependency "net-ssh", "< 3.0" # fog dependcy but net-ssh >= 3.x require ruby 2.x
|
43
|
+
spec.add_dependency "fog-google", "< 0.1.1" # fog dependcy but net-ssh >= 3.x require ruby 2.x
|
44
|
+
spec.add_dependency "fog-profitbricks", "~> 0.0.5" # fog dependency but fog-profitbricks > 0.5 requires ruby 2.x
|
45
|
+
spec.add_dependency "fog", "~> 1.36.0"
|
46
|
+
spec.add_dependency "gli", "~> 2.13.1"
|
47
|
+
spec.add_dependency "dop_common", "~> 0.13", '>= 0.13.0'
|
48
|
+
spec.add_dependency 'parallel', '~> 1'
|
49
|
+
if RUBY_VERSION < "2"
|
50
|
+
spec.add_dependency "rest-client", "< 2.0"
|
51
|
+
end
|
52
|
+
end
|
data/lib/dopv.rb
ADDED
@@ -0,0 +1,166 @@
|
|
1
|
+
require 'dopv/version'
|
2
|
+
require 'dopv/log'
|
3
|
+
require 'dopv/persistent_disk'
|
4
|
+
require 'dopv/plan'
|
5
|
+
require 'dopv/infrastructure'
|
6
|
+
require 'dopv/state_store'
|
7
|
+
require 'dop_common'
|
8
|
+
require 'etc'
|
9
|
+
require 'parallel'
|
10
|
+
|
11
|
+
module Dopv
|
12
|
+
extend DopCommon::NodeFilter
|
13
|
+
|
14
|
+
DEFAULT_MAX_IN_FLIGHT = 5
|
15
|
+
|
16
|
+
def self.valid?(plan_file)
|
17
|
+
hash, _ = plan_store.read_plan_file(plan_file)
|
18
|
+
plan = DopCommon::Plan.new(hash)
|
19
|
+
plan.valid?
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.add(plan_file)
|
23
|
+
raise StandardError, 'Plan not valid; did not add' unless valid?(plan_file)
|
24
|
+
plan_name = plan_store.add(plan_file)
|
25
|
+
Dopv.update_state(plan_name)
|
26
|
+
plan_name
|
27
|
+
end
|
28
|
+
|
29
|
+
def self.update_plan(plan_file, options = {})
|
30
|
+
raise StandardError, 'Plan not valid; did not add' unless valid?(plan_file)
|
31
|
+
plan_name = plan_store.update(plan_file)
|
32
|
+
update_state(plan_name, options)
|
33
|
+
plan_name
|
34
|
+
end
|
35
|
+
|
36
|
+
def self.update_state(plan_name, options = {})
|
37
|
+
plan_store.run_lock(plan_name) do
|
38
|
+
state_store = Dopv::StateStore.new(plan_name, plan_store)
|
39
|
+
state_store.update(options)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def self.remove(plan_name, remove_dopi_state = true, remove_dopv_state = false)
|
44
|
+
plan_store.remove(plan_name, remove_dopi_state, remove_dopv_state)
|
45
|
+
end
|
46
|
+
|
47
|
+
def self.list
|
48
|
+
plan_store.list
|
49
|
+
end
|
50
|
+
|
51
|
+
def self.deploy(plan_name, options = {})
|
52
|
+
run(:deploy, plan_name, options)
|
53
|
+
end
|
54
|
+
|
55
|
+
def self.undeploy(plan_name, options = {})
|
56
|
+
run(:undeploy, plan_name, options)
|
57
|
+
end
|
58
|
+
|
59
|
+
def self.refresh(plan_name, options = {})
|
60
|
+
run(:refresh, plan_name, options)
|
61
|
+
end
|
62
|
+
|
63
|
+
def self.export_state(plan_name)
|
64
|
+
ensure_plan_exists(plan_name)
|
65
|
+
state_store = Dopv::StateStore.new(plan_name, plan_store)
|
66
|
+
state_store.export
|
67
|
+
end
|
68
|
+
|
69
|
+
def self.export_state_file(plan_name, state_file)
|
70
|
+
ensure_plan_exists(plan_name)
|
71
|
+
File.open(state_file, 'w+') do |diskdb|
|
72
|
+
diskdb << YAML.dump(Dopv.export_state(plan_name))
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
def self.import_state(plan_name, data_volumes_db)
|
77
|
+
ensure_plan_exists(plan_name)
|
78
|
+
plan_store.run_lock(plan_name) do
|
79
|
+
state_store = Dopv::StateStore.new(plan_name, plan_store)
|
80
|
+
state_store.import(data_volumes_db)
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
def self.import_state_file(plan_name, state_file)
|
85
|
+
ensure_plan_exists(plan_name)
|
86
|
+
Dopv.import_state(plan_name, YAML.load_file(state_file))
|
87
|
+
end
|
88
|
+
|
89
|
+
private
|
90
|
+
|
91
|
+
def self.plan_store
|
92
|
+
@plan_store ||= DopCommon::PlanStore.new(DopCommon.config.plan_store_dir)
|
93
|
+
end
|
94
|
+
|
95
|
+
def self.ensure_plan_exists(plan_name)
|
96
|
+
unless plan_store.list.include?(plan_name)
|
97
|
+
raise StandardError, "The plan #{plan_name} does not exist in the plan store"
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
def self.get_plan(plan_name)
|
102
|
+
raise StandardError, 'Please update the plan state, there are pending updates' if pending_updates?(plan_name)
|
103
|
+
plan_parser = plan_store.get_plan(plan_name)
|
104
|
+
Dopv::Plan.new(plan_parser)
|
105
|
+
end
|
106
|
+
|
107
|
+
def self.pending_updates?(plan_name)
|
108
|
+
state_store = Dopv::StateStore.new(plan_name, plan_store)
|
109
|
+
state_store.pending_updates?
|
110
|
+
end
|
111
|
+
|
112
|
+
def self.run(operation, plan_name, options)
|
113
|
+
ensure_plan_exists(plan_name)
|
114
|
+
update_state(plan_name)
|
115
|
+
plan = get_plan(plan_name)
|
116
|
+
run_options = merge_default_options(options)
|
117
|
+
nodes = filter_nodes(plan.nodes, run_options[:run_for_nodes])
|
118
|
+
|
119
|
+
context_log_path = File.join(DopCommon.config.log_dir, "#{run_options[:run_id]}-#{plan_name}")
|
120
|
+
node_names = nodes.map{|n| n.name}
|
121
|
+
context_logger = DopCommon::ThreadContextLogger.new(context_log_path, node_names)
|
122
|
+
|
123
|
+
plan_store.run_lock(plan_name) do
|
124
|
+
state_store = Dopv::StateStore.new(plan_name, plan_store)
|
125
|
+
in_parallel(plan, nodes) do |node|
|
126
|
+
context_logger.log_context = node.name
|
127
|
+
case operation
|
128
|
+
when :deploy then Dopv::Infrastructure::bootstrap_node(node, state_store)
|
129
|
+
when :undeploy then Dopv::Infrastructure::destroy_node(node, state_store, run_options[:rmdisk])
|
130
|
+
when :refresh then Dopv::Infrastructure::refresh_node(node, state_store)
|
131
|
+
end
|
132
|
+
end
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
def self.merge_default_options(options)
|
137
|
+
{
|
138
|
+
:run_for_nodes => :all,
|
139
|
+
:rmdisk => false,
|
140
|
+
:run_id => Time.now.strftime('%Y%m%d-%H%M%S'),
|
141
|
+
}.merge(options)
|
142
|
+
end
|
143
|
+
|
144
|
+
def self.in_parallel(plan, nodes)
|
145
|
+
errors = false
|
146
|
+
infras = nodes.group_by {|node| node.infrastructure}
|
147
|
+
Parallel.each(infras.keys, :in_threads => infras.keys.length) do |infra|
|
148
|
+
Dopv.log.debug("Spawning control thread for infra #{infra.name}")
|
149
|
+
max_in_flight = infra.max_in_flight || plan.max_in_flight || DEFAULT_MAX_IN_FLIGHT
|
150
|
+
Dopv.log.debug("Threads for infra #{infra.name}: #{max_in_flight}")
|
151
|
+
Parallel.each(infras[infra], :in_threads => max_in_flight) do |node|
|
152
|
+
Dopv.log.debug("Spawning thread for node #{node.name}.")
|
153
|
+
begin
|
154
|
+
Dopv.log.debug("Yielding node #{node.name}.")
|
155
|
+
yield(node)
|
156
|
+
rescue => e
|
157
|
+
errors = true
|
158
|
+
Dopv.log.error("There was an error while processing node #{node.name}: #{e}")
|
159
|
+
raise Parallel::Break
|
160
|
+
end
|
161
|
+
end
|
162
|
+
end
|
163
|
+
raise "Errors detected during plan run" if errors
|
164
|
+
end
|
165
|
+
|
166
|
+
end
|
data/lib/dopv/cli.rb
ADDED
@@ -0,0 +1,54 @@
|
|
1
|
+
#
|
2
|
+
# DOPv command line main module
|
3
|
+
#
|
4
|
+
|
5
|
+
require 'gli'
|
6
|
+
require 'dop_common/cli/node_selection'
|
7
|
+
require 'dop_common/cli/log'
|
8
|
+
require 'dop_common/cli/global_options'
|
9
|
+
require 'dopv'
|
10
|
+
require 'dopv/cli/command_validate'
|
11
|
+
require 'dopv/cli/command_add'
|
12
|
+
require 'dopv/cli/command_remove'
|
13
|
+
require 'dopv/cli/command_list'
|
14
|
+
require 'dopv/cli/command_update'
|
15
|
+
require 'dopv/cli/command_import'
|
16
|
+
require 'dopv/cli/command_export'
|
17
|
+
require 'dopv/cli/command_run'
|
18
|
+
require 'logger/colors'
|
19
|
+
|
20
|
+
module Dopv
|
21
|
+
module Cli
|
22
|
+
include GLI::App
|
23
|
+
extend self
|
24
|
+
|
25
|
+
trace = false
|
26
|
+
|
27
|
+
program_desc 'DOPv command line tool'
|
28
|
+
version Dopv::VERSION
|
29
|
+
|
30
|
+
subcommand_option_handling :normal
|
31
|
+
arguments :strict
|
32
|
+
|
33
|
+
DopCommon::Cli.global_options(self)
|
34
|
+
|
35
|
+
pre do |global,command,options,args|
|
36
|
+
DopCommon.configure = global
|
37
|
+
ENV['GLI_DEBUG'] = 'true' if global[:trace] == true
|
38
|
+
DopCommon::Cli.initialize_logger('dopv.log', global[:log_level], global[:verbosity], global[:trace])
|
39
|
+
true
|
40
|
+
end
|
41
|
+
|
42
|
+
command_validate(self)
|
43
|
+
command_add(self)
|
44
|
+
command_remove(self)
|
45
|
+
command_list(self)
|
46
|
+
command_update(self)
|
47
|
+
command_import(self)
|
48
|
+
command_export(self)
|
49
|
+
command_run(self, :deploy)
|
50
|
+
command_run(self, :undeploy)
|
51
|
+
command_run(self, :refresh)
|
52
|
+
|
53
|
+
end
|
54
|
+
end
|