dtk-shell 0.10.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 +7 -0
- data/Gemfile +5 -0
- data/Gemfile_dev +13 -0
- data/README.md +121 -0
- data/bin/dtk-execute +32 -0
- data/bin/dtk-run +92 -0
- data/bin/dtk-shell +31 -0
- data/dtk-shell.gemspec +50 -0
- data/lib/auxiliary.rb +61 -0
- data/lib/bundler_monkey_patch.rb +26 -0
- data/lib/client.rb +58 -0
- data/lib/command_helper.rb +33 -0
- data/lib/command_helpers/git_repo.rb +589 -0
- data/lib/command_helpers/git_repo/merge.rb +153 -0
- data/lib/command_helpers/jenkins_client.rb +106 -0
- data/lib/command_helpers/jenkins_client/config_xml.rb +288 -0
- data/lib/command_helpers/service_importer.rb +251 -0
- data/lib/command_helpers/service_link.rb +33 -0
- data/lib/command_helpers/test_module_creator.rb +69 -0
- data/lib/command_helpers/test_module_templates/dtk.model.yaml.eruby +10 -0
- data/lib/command_helpers/test_module_templates/spec_helper.rb.eruby +10 -0
- data/lib/command_helpers/test_module_templates/temp_component_spec.rb.eruby +5 -0
- data/lib/commands.rb +57 -0
- data/lib/commands/common/thor/access_control.rb +133 -0
- data/lib/commands/common/thor/action_result_handler.rb +74 -0
- data/lib/commands/common/thor/assembly_template.rb +92 -0
- data/lib/commands/common/thor/assembly_workspace.rb +1801 -0
- data/lib/commands/common/thor/base_command_helper.rb +59 -0
- data/lib/commands/common/thor/clone.rb +82 -0
- data/lib/commands/common/thor/common.rb +88 -0
- data/lib/commands/common/thor/common_base.rb +49 -0
- data/lib/commands/common/thor/create_target.rb +70 -0
- data/lib/commands/common/thor/edit.rb +255 -0
- data/lib/commands/common/thor/inventory_parser.rb +98 -0
- data/lib/commands/common/thor/list_diffs.rb +128 -0
- data/lib/commands/common/thor/module.rb +1011 -0
- data/lib/commands/common/thor/module/import.rb +210 -0
- data/lib/commands/common/thor/node.rb +53 -0
- data/lib/commands/common/thor/poller.rb +65 -0
- data/lib/commands/common/thor/pull_clone_changes.rb +28 -0
- data/lib/commands/common/thor/pull_from_remote.rb +152 -0
- data/lib/commands/common/thor/puppet_forge.rb +72 -0
- data/lib/commands/common/thor/purge_clone.rb +101 -0
- data/lib/commands/common/thor/push_clone_changes.rb +162 -0
- data/lib/commands/common/thor/push_to_remote.rb +94 -0
- data/lib/commands/common/thor/remotes.rb +71 -0
- data/lib/commands/common/thor/reparse.rb +40 -0
- data/lib/commands/common/thor/set_required_attributes.rb +46 -0
- data/lib/commands/thor/account.rb +239 -0
- data/lib/commands/thor/assembly.rb +356 -0
- data/lib/commands/thor/attribute.rb +79 -0
- data/lib/commands/thor/component.rb +70 -0
- data/lib/commands/thor/component_module.rb +501 -0
- data/lib/commands/thor/component_template.rb +174 -0
- data/lib/commands/thor/dependency.rb +34 -0
- data/lib/commands/thor/developer.rb +144 -0
- data/lib/commands/thor/dtk.rb +152 -0
- data/lib/commands/thor/library.rb +125 -0
- data/lib/commands/thor/node.rb +504 -0
- data/lib/commands/thor/node_template.rb +94 -0
- data/lib/commands/thor/project.rb +34 -0
- data/lib/commands/thor/provider.rb +233 -0
- data/lib/commands/thor/remotes.rb +49 -0
- data/lib/commands/thor/service.rb +941 -0
- data/lib/commands/thor/service_module.rb +914 -0
- data/lib/commands/thor/state_change.rb +25 -0
- data/lib/commands/thor/target.rb +250 -0
- data/lib/commands/thor/task.rb +116 -0
- data/lib/commands/thor/test_module.rb +310 -0
- data/lib/commands/thor/utils.rb +21 -0
- data/lib/commands/thor/workspace.rb +685 -0
- data/lib/config/cacert.pem +3785 -0
- data/lib/config/client.conf.header +20 -0
- data/lib/config/configuration.rb +99 -0
- data/lib/config/default.conf +16 -0
- data/lib/config/disk_cacher.rb +80 -0
- data/lib/configurator.rb +176 -0
- data/lib/context_router.rb +44 -0
- data/lib/core.rb +497 -0
- data/lib/domain/git_adapter.rb +412 -0
- data/lib/domain/git_error_handler.rb +64 -0
- data/lib/domain/response.rb +285 -0
- data/lib/domain/response/error_handler.rb +86 -0
- data/lib/dtk-shell/version.rb +20 -0
- data/lib/dtk_constants.rb +40 -0
- data/lib/dtk_error.rb +114 -0
- data/lib/dtk_logger.rb +126 -0
- data/lib/dtk_shell.rb +31 -0
- data/lib/error.rb +85 -0
- data/lib/execute.rb +29 -0
- data/lib/execute/cli_pure/cli_rerouter.rb +102 -0
- data/lib/execute/command.rb +40 -0
- data/lib/execute/command/api_call.rb +60 -0
- data/lib/execute/command/api_call/map.rb +60 -0
- data/lib/execute/command/api_call/service.rb +91 -0
- data/lib/execute/command/api_call/translation_term.rb +119 -0
- data/lib/execute/command/rest_call.rb +37 -0
- data/lib/execute/command_processor.rb +30 -0
- data/lib/execute/command_processor/rest_call.rb +59 -0
- data/lib/execute/error_usage.rb +21 -0
- data/lib/execute/execute_context.rb +86 -0
- data/lib/execute/execute_context/result_store.rb +37 -0
- data/lib/execute/script.rb +64 -0
- data/lib/execute/script/add_tenant.rb +121 -0
- data/lib/git-logs/git.log +0 -0
- data/lib/parser/adapters/option_parser.rb +70 -0
- data/lib/parser/adapters/thor.rb +555 -0
- data/lib/parser/adapters/thor/common_option_defs.rb +40 -0
- data/lib/require_first.rb +104 -0
- data/lib/search_hash.rb +44 -0
- data/lib/shell.rb +261 -0
- data/lib/shell/context.rb +1065 -0
- data/lib/shell/context_aux.rb +46 -0
- data/lib/shell/domain/active_context.rb +186 -0
- data/lib/shell/domain/context_entity.rb +89 -0
- data/lib/shell/domain/context_params.rb +223 -0
- data/lib/shell/domain/override_tasks.rb +88 -0
- data/lib/shell/domain/shadow_entity.rb +76 -0
- data/lib/shell/header_shell.rb +44 -0
- data/lib/shell/help_monkey_patch.rb +283 -0
- data/lib/shell/interactive_wizard.rb +225 -0
- data/lib/shell/message_queue.rb +63 -0
- data/lib/shell/parse_monkey_patch.rb +39 -0
- data/lib/shell/status_monitor.rb +124 -0
- data/lib/task_status.rb +83 -0
- data/lib/task_status/refresh_mode.rb +77 -0
- data/lib/task_status/snapshot_mode.rb +28 -0
- data/lib/task_status/stream_mode.rb +48 -0
- data/lib/task_status/stream_mode/element.rb +101 -0
- data/lib/task_status/stream_mode/element/format.rb +101 -0
- data/lib/task_status/stream_mode/element/hierarchical_task.rb +100 -0
- data/lib/task_status/stream_mode/element/hierarchical_task/result.rb +72 -0
- data/lib/task_status/stream_mode/element/hierarchical_task/result/action.rb +93 -0
- data/lib/task_status/stream_mode/element/hierarchical_task/result/components.rb +26 -0
- data/lib/task_status/stream_mode/element/hierarchical_task/result/node_level.rb +26 -0
- data/lib/task_status/stream_mode/element/hierarchical_task/steps.rb +34 -0
- data/lib/task_status/stream_mode/element/hierarchical_task/steps/action.rb +53 -0
- data/lib/task_status/stream_mode/element/hierarchical_task/steps/components.rb +53 -0
- data/lib/task_status/stream_mode/element/hierarchical_task/steps/node_level.rb +42 -0
- data/lib/task_status/stream_mode/element/no_results.rb +26 -0
- data/lib/task_status/stream_mode/element/render.rb +59 -0
- data/lib/task_status/stream_mode/element/stage.rb +84 -0
- data/lib/task_status/stream_mode/element/stage/render.rb +76 -0
- data/lib/task_status/stream_mode/element/task_end.rb +35 -0
- data/lib/task_status/stream_mode/element/task_start.rb +37 -0
- data/lib/util/common_util.rb +37 -0
- data/lib/util/console.rb +235 -0
- data/lib/util/dtk_puppet.rb +65 -0
- data/lib/util/module_util.rb +66 -0
- data/lib/util/os_util.rb +385 -0
- data/lib/util/permission_util.rb +31 -0
- data/lib/util/remote_dependency_util.rb +84 -0
- data/lib/util/ssh_util.rb +94 -0
- data/lib/view_processor.rb +129 -0
- data/lib/view_processor/augmented_simple_list.rb +44 -0
- data/lib/view_processor/hash_pretty_print.rb +123 -0
- data/lib/view_processor/simple_list.rb +156 -0
- data/lib/view_processor/table_print.rb +309 -0
- data/lib/violation.rb +86 -0
- data/lib/violation/attribute.rb +76 -0
- data/lib/violation/fix.rb +26 -0
- data/lib/violation/fix/result.rb +73 -0
- data/lib/violation/fix/result/error.rb +34 -0
- data/lib/violation/fix/set_attribute.rb +41 -0
- data/lib/violation/sub_classes.rb +60 -0
- data/puppet/manifests/init.pp +72 -0
- data/puppet/manifests/params.pp +16 -0
- data/puppet/r8meta.puppet.yml +35 -0
- data/puppet/templates/bash_profile.erb +2 -0
- data/puppet/templates/client.conf.erb +1 -0
- data/puppet/templates/dtkclient.erb +2 -0
- data/spec/component_module_spec.rb +34 -0
- data/spec/dependency_spec.rb +6 -0
- data/spec/dtk_shell_spec.rb +13 -0
- data/spec/dtk_spec.rb +33 -0
- data/spec/lib/spec_helper.rb +10 -0
- data/spec/lib/spec_thor.rb +108 -0
- data/spec/node_template_spec.rb +24 -0
- data/spec/project_spec.rb +6 -0
- data/spec/repo_spec.rb +7 -0
- data/spec/response_spec.rb +52 -0
- data/spec/service_module_spec.rb +38 -0
- data/spec/service_spec.rb +50 -0
- data/spec/state_change_spec.rb +7 -0
- data/spec/table_print_spec.rb +48 -0
- data/spec/target_spec.rb +57 -0
- data/spec/task_spec.rb +28 -0
- data/views/assembly/augmented_simple_list.rb +12 -0
- data/views/assembly_template/augmented_simple_list.rb +12 -0
- data/views/list_task/augmented_simple_list.rb +12 -0
- metadata +421 -0
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
#
|
|
2
|
+
# Copyright (C) 2010-2016 dtk contributors
|
|
3
|
+
#
|
|
4
|
+
# This file is part of the dtk project.
|
|
5
|
+
#
|
|
6
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
7
|
+
# you may not use this file except in compliance with the License.
|
|
8
|
+
# You may obtain a copy of the License at
|
|
9
|
+
#
|
|
10
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
11
|
+
#
|
|
12
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
13
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
14
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
15
|
+
# See the License for the specific language governing permissions and
|
|
16
|
+
# limitations under the License.
|
|
17
|
+
#
|
|
18
|
+
class DTK::Client::Execute
|
|
19
|
+
class ExecuteContext
|
|
20
|
+
class ResultStore < Hash
|
|
21
|
+
def store(result,result_index=nil)
|
|
22
|
+
self[result_index||Index.last] = result
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def get_last_result?()
|
|
26
|
+
self[Index.last]
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
module Index
|
|
30
|
+
def self.last()
|
|
31
|
+
Last
|
|
32
|
+
end
|
|
33
|
+
Last = :last
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
#
|
|
2
|
+
# Copyright (C) 2010-2016 dtk contributors
|
|
3
|
+
#
|
|
4
|
+
# This file is part of the dtk project.
|
|
5
|
+
#
|
|
6
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
7
|
+
# you may not use this file except in compliance with the License.
|
|
8
|
+
# You may obtain a copy of the License at
|
|
9
|
+
#
|
|
10
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
11
|
+
#
|
|
12
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
13
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
14
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
15
|
+
# See the License for the specific language governing permissions and
|
|
16
|
+
# limitations under the License.
|
|
17
|
+
#
|
|
18
|
+
class DTK::Client::Execute
|
|
19
|
+
class Script < self
|
|
20
|
+
dtk_require('script/add_tenant')
|
|
21
|
+
def self.execute()
|
|
22
|
+
script_class().execute_script()
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
private
|
|
26
|
+
Scripts = {
|
|
27
|
+
'add-tenant' => AddTenant,
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
def self.script_class()
|
|
31
|
+
script_name = ARGV.size > 0 && ARGV[0]
|
|
32
|
+
unless script_class = script_name && Scripts[script_name]
|
|
33
|
+
legal_scripts = Scripts.keys
|
|
34
|
+
err_msg = (script_name ? "Unsupported script '#{script_name}'" : "Missing script name")
|
|
35
|
+
err_msg << "; supported scripts are (#{legal_scripts.join(',')})"
|
|
36
|
+
raise ErrorUsage.new(err_msg)
|
|
37
|
+
end
|
|
38
|
+
script_class
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def self.execute_script()
|
|
42
|
+
params = ret_params_from_argv()
|
|
43
|
+
execute_with_params(params)
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
module OptionParserHelper
|
|
47
|
+
require 'optparse'
|
|
48
|
+
def process_params_from_options(banner,&block)
|
|
49
|
+
OptionParser.new do |opts|
|
|
50
|
+
opts.banner = banner
|
|
51
|
+
block.call(opts)
|
|
52
|
+
end.parse!
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def show_help_and_exit(banner)
|
|
56
|
+
ARGV[0] = '--help'
|
|
57
|
+
OptionParser.new do |opts|
|
|
58
|
+
opts.banner = banner
|
|
59
|
+
end.parse!
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
extend OptionParserHelper
|
|
63
|
+
end
|
|
64
|
+
end
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
#
|
|
2
|
+
# Copyright (C) 2010-2016 dtk contributors
|
|
3
|
+
#
|
|
4
|
+
# This file is part of the dtk project.
|
|
5
|
+
#
|
|
6
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
7
|
+
# you may not use this file except in compliance with the License.
|
|
8
|
+
# You may obtain a copy of the License at
|
|
9
|
+
#
|
|
10
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
11
|
+
#
|
|
12
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
13
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
14
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
15
|
+
# See the License for the specific language governing permissions and
|
|
16
|
+
# limitations under the License.
|
|
17
|
+
#
|
|
18
|
+
class DTK::Client::Execute::Script
|
|
19
|
+
class AddTenant < self
|
|
20
|
+
def self.ret_params_from_argv()
|
|
21
|
+
banner = "Usage: dtk-execute add-tenant TENANT-NAME CATALOG-USERNAME -p PASSWORD -v VERSION [-s SERVICE-INSTANCE]"
|
|
22
|
+
version = tenant_name = catalog_username = tenant_number = nil
|
|
23
|
+
if ARGV.size > 3
|
|
24
|
+
tenant_name = ARGV[1]
|
|
25
|
+
if tenant_name =~ /^dtk([0-9]+)$/
|
|
26
|
+
tenant_number = $1
|
|
27
|
+
else
|
|
28
|
+
raise ErrorUsage.new("TENANT-NAME must be of form 'dtkNUM', like dtk601")
|
|
29
|
+
end
|
|
30
|
+
catalog_username = ARGV[2]
|
|
31
|
+
else
|
|
32
|
+
show_help_and_exit(banner)
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
params = Hash.new
|
|
36
|
+
process_params_from_options(banner) do |opts|
|
|
37
|
+
opts.on( '-p', '--password PASSWORD', "Password for tenant and catalog" ) do |pw|
|
|
38
|
+
params[:password] = pw
|
|
39
|
+
end
|
|
40
|
+
opts.on( '-s', '--service SERVICE-INSTANCE', "Name of Service instance" ) do |s|
|
|
41
|
+
params[:service_instance] = s
|
|
42
|
+
end
|
|
43
|
+
opts.on( '-v', '--version VERSION', "Version of code" ) do |v|
|
|
44
|
+
params[:version] = v
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
# TODO: use opt parser to enforce that :password and :version options are manditory
|
|
49
|
+
unless params[:password]
|
|
50
|
+
raise ErrorUsage.new("Password is mandatory; use -p commnd line option")
|
|
51
|
+
end
|
|
52
|
+
unless params[:version]
|
|
53
|
+
raise ErrorUsage.new("Version is mandatory; use -v commnd line option")
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
service_instance = params[:service_instance] || "dtkhost#{tenant_number[0]}"
|
|
57
|
+
to_add = {
|
|
58
|
+
:tenant_name => tenant_name,
|
|
59
|
+
:catalog_username => catalog_username,
|
|
60
|
+
:service_instance => service_instance
|
|
61
|
+
}
|
|
62
|
+
params.merge(to_add)
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def self.execute_with_params(params)
|
|
66
|
+
tenant_name = params[:tenant_name]
|
|
67
|
+
catalog_username = params[:catalog_username]
|
|
68
|
+
service = params[:service_instance]
|
|
69
|
+
password = params[:password]
|
|
70
|
+
version = params[:version]
|
|
71
|
+
server_node = params[:server_node_name] || 'server'
|
|
72
|
+
router_node = params[:router_node_name] || 'router'
|
|
73
|
+
|
|
74
|
+
component_with_namespace = "dtk-meta-user:dtk_tenant[#{tenant_name}]"
|
|
75
|
+
component_namespace, component = (component_with_namespace =~ /(^[^:]+):(.+$)/; [$1,$2])
|
|
76
|
+
|
|
77
|
+
av_pairs = {
|
|
78
|
+
:catalog_username => catalog_username,
|
|
79
|
+
:tenant_password => password,
|
|
80
|
+
:catalog_password => password,
|
|
81
|
+
:server_branch => version,
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
linked_component_instances =
|
|
85
|
+
[
|
|
86
|
+
"#{router_node}/dtk_nginx::vhosts_for_router",
|
|
87
|
+
"#{server_node}/dtk_postgresql::databases",
|
|
88
|
+
"#{server_node}/host::hostname"
|
|
89
|
+
]
|
|
90
|
+
|
|
91
|
+
ExecuteContext(:print_results => true) do
|
|
92
|
+
result = call 'service/add_component',
|
|
93
|
+
:service => service,
|
|
94
|
+
:node => server_node,
|
|
95
|
+
:component => component,
|
|
96
|
+
:namespace => component_namespace,
|
|
97
|
+
:donot_update_workflow => true
|
|
98
|
+
|
|
99
|
+
av_pairs.each_pair do |a,v|
|
|
100
|
+
result = call 'service/set_attribute',
|
|
101
|
+
:service => service,
|
|
102
|
+
:attribute_path => "#{server_node}/#{component}/#{a}",
|
|
103
|
+
:value => v
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
linked_component_instances.each do |linked_component_instance|
|
|
107
|
+
result = call 'service/link_components',
|
|
108
|
+
:service => service,
|
|
109
|
+
:input_component => "#{server_node}/#{component}",
|
|
110
|
+
:output_component => linked_component_instance
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
result = call 'service/execute_workflow',
|
|
114
|
+
:service => service,
|
|
115
|
+
:workflow_name => 'add_tenant',
|
|
116
|
+
:workflow_params => {'name' => tenant_name}
|
|
117
|
+
|
|
118
|
+
end
|
|
119
|
+
end
|
|
120
|
+
end
|
|
121
|
+
end
|
|
File without changes
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
#
|
|
2
|
+
# Copyright (C) 2010-2016 dtk contributors
|
|
3
|
+
#
|
|
4
|
+
# This file is part of the dtk project.
|
|
5
|
+
#
|
|
6
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
7
|
+
# you may not use this file except in compliance with the License.
|
|
8
|
+
# You may obtain a copy of the License at
|
|
9
|
+
#
|
|
10
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
11
|
+
#
|
|
12
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
13
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
14
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
15
|
+
# See the License for the specific language governing permissions and
|
|
16
|
+
# limitations under the License.
|
|
17
|
+
#
|
|
18
|
+
require 'optparse'
|
|
19
|
+
module DTK
|
|
20
|
+
module Client
|
|
21
|
+
class CommandBaseOptionParser
|
|
22
|
+
include CommandBase
|
|
23
|
+
def initialize(conn)
|
|
24
|
+
@conn = conn
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def self.execute_from_cli(conn,argv,shell_execute=false)
|
|
28
|
+
return conn.connection_error if conn.connection_error
|
|
29
|
+
method, args_hash = OptionParser.parse_options(self,argv)
|
|
30
|
+
instance = new(conn)
|
|
31
|
+
raise Error.new("Illegal subcommand #{method}") unless instance.respond_to?(method)
|
|
32
|
+
instance.send(method,args_hash)
|
|
33
|
+
end
|
|
34
|
+
class << self
|
|
35
|
+
include Auxiliary
|
|
36
|
+
def command_name()
|
|
37
|
+
snake_form(self,"-")
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
class OptionParser
|
|
42
|
+
def self.parse_options(command_class,argv)
|
|
43
|
+
args_hash = Hash.new
|
|
44
|
+
unless subcommand = argv[0]
|
|
45
|
+
raise Error.new("No subcommand given")
|
|
46
|
+
end
|
|
47
|
+
method = subcommand.to_sym
|
|
48
|
+
unless parse_info = (command_class.const_get "CLIParseOptions")[subcommand.to_sym]
|
|
49
|
+
return [method,args_hash]
|
|
50
|
+
end
|
|
51
|
+
::OptionParser.new do|opts|
|
|
52
|
+
opts.banner = "Usage: #{command_class.command_name} #{subcommand} [options]"
|
|
53
|
+
(parse_info[:options]||[]).each do |parse_info_option|
|
|
54
|
+
raise Error.new("missing param name") unless param_name = parse_info_option[:name]
|
|
55
|
+
raise Error.new("missing optparse spec") unless parse_info_option[:optparse_spec]
|
|
56
|
+
opts.on(*parse_info_option[:optparse_spec]) do |val|
|
|
57
|
+
args_hash[param_name.to_s] = val ? val : true
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
opts.on('-h', '--help', 'Display this screen') do
|
|
62
|
+
puts opts
|
|
63
|
+
exit
|
|
64
|
+
end
|
|
65
|
+
end.parse!(argv)
|
|
66
|
+
[method,args_hash]
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
end
|
|
@@ -0,0 +1,555 @@
|
|
|
1
|
+
#
|
|
2
|
+
# Copyright (C) 2010-2016 dtk contributors
|
|
3
|
+
#
|
|
4
|
+
# This file is part of the dtk project.
|
|
5
|
+
#
|
|
6
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
7
|
+
# you may not use this file except in compliance with the License.
|
|
8
|
+
# You may obtain a copy of the License at
|
|
9
|
+
#
|
|
10
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
11
|
+
#
|
|
12
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
13
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
14
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
15
|
+
# See the License for the specific language governing permissions and
|
|
16
|
+
# limitations under the License.
|
|
17
|
+
#
|
|
18
|
+
require 'thor'
|
|
19
|
+
require 'thor/group'
|
|
20
|
+
require 'readline'
|
|
21
|
+
require 'colorize'
|
|
22
|
+
require 'digest/sha1'
|
|
23
|
+
|
|
24
|
+
dtk_require("../../shell/interactive_wizard")
|
|
25
|
+
dtk_require("../../util/os_util")
|
|
26
|
+
dtk_require("../../util/console")
|
|
27
|
+
dtk_require_from_base('task_status')
|
|
28
|
+
dtk_require_from_base("command_helper")
|
|
29
|
+
dtk_require("../../context_router")
|
|
30
|
+
dtk_require_common_commands('thor/poller')
|
|
31
|
+
|
|
32
|
+
module DTK
|
|
33
|
+
module Client
|
|
34
|
+
class CommandBaseThor < Thor
|
|
35
|
+
dtk_nested_require('thor','common_option_defs')
|
|
36
|
+
|
|
37
|
+
include CommandBase
|
|
38
|
+
include CommandHelperMixin
|
|
39
|
+
include Poller
|
|
40
|
+
|
|
41
|
+
extend CommandBase
|
|
42
|
+
extend TaskStatusMixin
|
|
43
|
+
extend Console
|
|
44
|
+
extend CommonOptionDefs::ClassMixin
|
|
45
|
+
|
|
46
|
+
@@cached_response = {}
|
|
47
|
+
@@invalidate_map = []
|
|
48
|
+
TIME_DIFF = 60 # second(s)
|
|
49
|
+
EXTENDED_TIMEOUT = 360 # second(s)
|
|
50
|
+
HIDE_FROM_BASE_CONTEXT = "HIDE_FROM_BASE"
|
|
51
|
+
|
|
52
|
+
# thor command specific constants
|
|
53
|
+
ALT_IDENTIFIER_SEPARATOR = ':::'
|
|
54
|
+
|
|
55
|
+
def initialize(args, opts, config)
|
|
56
|
+
@conn = config[:conn]
|
|
57
|
+
super
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def self.execute_from_cli(conn,method_name,context_params,thor_options,shell_execution=false)
|
|
61
|
+
@@shell_execution = shell_execution
|
|
62
|
+
|
|
63
|
+
if method_name == 'help'
|
|
64
|
+
ret = start([method_name] + context_params.method_arguments,:conn => conn)
|
|
65
|
+
else
|
|
66
|
+
ret = start([method_name, context_params] + (thor_options||[]),:conn => conn)
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
# special case where we have validation reply
|
|
70
|
+
if ret.kind_of?(Response)
|
|
71
|
+
if ret.validation_response?
|
|
72
|
+
ret = action_on_revalidation_response(ret, conn, method_name, context_params, thor_options, shell_execution)
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
ret.kind_of?(Response) ? ret : Response::NoOp.new
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
# TODO: Make sure that server responds with new format of ARGVS
|
|
80
|
+
def self.action_on_revalidation_response(validation_response, conn, method_name, context_params, thor_options, shell_execution)
|
|
81
|
+
puts "[NOTICE] #{validation_response.validation_message}"
|
|
82
|
+
actions = validation_response.validation_actions
|
|
83
|
+
|
|
84
|
+
actions.each_with_index do |action, i|
|
|
85
|
+
if Console.confirmation_prompt("Pre-action '#{action['action']}' needed, execute"+"?")
|
|
86
|
+
# we have hash map with values { :assembly_id => 2123123123, :option_1 => true }
|
|
87
|
+
# we translate to array of values, with action as first element
|
|
88
|
+
|
|
89
|
+
# def self.execute_from_cli(conn,method_name,context_params,options_args,shell_execution=false)
|
|
90
|
+
response = self.execute_from_cli(conn, action['action'], create_context_arguments(action['params']),[],shell_execution)
|
|
91
|
+
# we abort if error happens
|
|
92
|
+
ResponseErrorHandler.check(response)
|
|
93
|
+
|
|
94
|
+
if action['wait_for_complete']
|
|
95
|
+
entity_id, entity_type = action['wait_for_complete']['id'].to_s, action['wait_for_complete']['type']
|
|
96
|
+
puts "Waiting for task to complete ..."
|
|
97
|
+
task_status_aux(entity_id,entity_type,:wait => true)
|
|
98
|
+
end
|
|
99
|
+
else
|
|
100
|
+
# validation action are being skipped
|
|
101
|
+
return ""
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
puts "Executing original action: '#{method_name}' ..."
|
|
106
|
+
# if all executed correctly we run original action
|
|
107
|
+
return self.execute_from_cli(conn,method_name, context_params,thor_options,shell_execution)
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
def self.invalidate_entities(*array_of_entites)
|
|
111
|
+
# we handle to cases here
|
|
112
|
+
# n-context invalidation, whole structure
|
|
113
|
+
@@invalidate_map << array_of_entites.join('_').to_sym
|
|
114
|
+
|
|
115
|
+
# last entity
|
|
116
|
+
@@invalidate_map << array_of_entites.last.to_sym
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
# we take current timestamp and compare it to timestamp stored in @@cached_response
|
|
120
|
+
# if difference is greater than TIME_DIFF we send request again, if not we use
|
|
121
|
+
# response from cache
|
|
122
|
+
def self.get_cached_response(entity_name, url, subtype={})
|
|
123
|
+
subtype ||= {}
|
|
124
|
+
current_ts = Time.now.to_i
|
|
125
|
+
cache_id = (subtype.empty? ? :response : generate_cached_id(subtype))
|
|
126
|
+
|
|
127
|
+
# if @@cache_response is empty return true if not than return time difference between
|
|
128
|
+
# current_ts and ts stored in cache
|
|
129
|
+
time_difference = @@cached_response[entity_name].nil? ? true : ((current_ts - @@cached_response[entity_name][:ts]) > TIME_DIFF)
|
|
130
|
+
|
|
131
|
+
if (@@cached_response[entity_name])
|
|
132
|
+
time_difference = true if @@cached_response[entity_name][cache_id].nil?
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
if (time_difference || @@invalidate_map.include?(entity_name))
|
|
136
|
+
response = post rest_url(url), subtype
|
|
137
|
+
|
|
138
|
+
# we do not want to catch is if it is not valid
|
|
139
|
+
if response.nil? || response.empty?
|
|
140
|
+
DtkLogger.instance.debug("Response was nil or empty for that reason we did not cache it.")
|
|
141
|
+
return response
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
if response.ok?
|
|
145
|
+
response_hash = {cache_id => response, :ts => current_ts}
|
|
146
|
+
|
|
147
|
+
@@invalidate_map.delete(entity_name) if @@invalidate_map.include?(entity_name)
|
|
148
|
+
|
|
149
|
+
if @@cached_response[entity_name]
|
|
150
|
+
@@cached_response[entity_name].merge!(response_hash)
|
|
151
|
+
else
|
|
152
|
+
@@cached_response.store(entity_name, response_hash)
|
|
153
|
+
end
|
|
154
|
+
end
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
if @@cached_response[entity_name]
|
|
158
|
+
return @@cached_response[entity_name][cache_id]
|
|
159
|
+
else
|
|
160
|
+
return nil
|
|
161
|
+
end
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
def self.generate_cached_id(subtype)
|
|
165
|
+
values = ''
|
|
166
|
+
# subtype.sort.map do |key,value|
|
|
167
|
+
# removed sort since subtype is hash where keys are symbols,
|
|
168
|
+
# sort method uses the <=> comparison operator to put things into order but
|
|
169
|
+
# symbols don't have a <=> comparison operator in ruby 1.8.7
|
|
170
|
+
subtype.map do |key,value|
|
|
171
|
+
values += value.to_s
|
|
172
|
+
end
|
|
173
|
+
|
|
174
|
+
Digest::SHA1.hexdigest(values)
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
def self.create_context_arguments(params)
|
|
178
|
+
context_params = DTK::Shell::ContextParams.new
|
|
179
|
+
params.each do |k,v|
|
|
180
|
+
context_params.add_context_to_params(k,k,v)
|
|
181
|
+
end
|
|
182
|
+
return context_params
|
|
183
|
+
end
|
|
184
|
+
|
|
185
|
+
def self.list_method_supported?
|
|
186
|
+
return (respond_to?(:validation_list) || respond_to?(:whoami))
|
|
187
|
+
end
|
|
188
|
+
|
|
189
|
+
# returns all task names for given thor class with use friendly names (with '-' instead '_')
|
|
190
|
+
def self.task_names
|
|
191
|
+
all_tasks().map(&:first).collect { |item| item.gsub('_','-')}
|
|
192
|
+
end
|
|
193
|
+
|
|
194
|
+
def self.get_usage_info(entity_name, method)
|
|
195
|
+
# no need for nil checks since task will be found
|
|
196
|
+
# [0] element contains desire usage description
|
|
197
|
+
# [2] element contains method name with '_'
|
|
198
|
+
result = printable_tasks().select { |help_item| help_item[2].gsub('_','-') == method }.flatten[0]
|
|
199
|
+
# we add entity name with dashes
|
|
200
|
+
return result.gsub('dtk', "dtk #{entity_name.gsub('_','-')}")
|
|
201
|
+
end
|
|
202
|
+
|
|
203
|
+
# caches all the taks names for each possible tier and each thor class
|
|
204
|
+
# returnes it, executes only once and only on dtk-shell start
|
|
205
|
+
def self.tiered_task_names
|
|
206
|
+
# cached data
|
|
207
|
+
cached_tasks = {}
|
|
208
|
+
|
|
209
|
+
# get command name from namespace (which is derived by thor from file name)
|
|
210
|
+
command = namespace.split(':').last.gsub('_','-').upcase
|
|
211
|
+
|
|
212
|
+
# first elvel identifier
|
|
213
|
+
command_sym = command.downcase.to_sym
|
|
214
|
+
command_id_sym = (command.downcase + '_wid').to_sym
|
|
215
|
+
|
|
216
|
+
cached_tasks.store(command_sym, [])
|
|
217
|
+
cached_tasks.store(command_id_sym, [])
|
|
218
|
+
|
|
219
|
+
# n-context children
|
|
220
|
+
all_children = []
|
|
221
|
+
children = self.respond_to?(:all_children) ? self.all_children() : nil
|
|
222
|
+
all_children << children unless children.nil?
|
|
223
|
+
|
|
224
|
+
# some commands have multiple n-level contexts
|
|
225
|
+
# e.g. workspace_node_component, workspace_utils and workspace_node_utils
|
|
226
|
+
# we go through all of them and load them to 'all_children'
|
|
227
|
+
multi_context_children = self.respond_to?(:multi_context_children) ? self.multi_context_children() : nil
|
|
228
|
+
if multi_context_children
|
|
229
|
+
multi_context_children.each do |mc|
|
|
230
|
+
all_children << (mc.is_a?(Array) ? mc : multi_context_children)
|
|
231
|
+
end
|
|
232
|
+
end
|
|
233
|
+
|
|
234
|
+
# n-context-override task, special case which
|
|
235
|
+
override_task_obj = self.respond_to?(:override_allowed_methods) ? self.override_allowed_methods.dup : nil
|
|
236
|
+
|
|
237
|
+
# we seperate tier 1 and tier 2 tasks
|
|
238
|
+
all_tasks().each do |task|
|
|
239
|
+
# noralize task name with '-' since it will be displayed to user that way
|
|
240
|
+
task_name = task[0].gsub('_','-')
|
|
241
|
+
usage = task[1].usage
|
|
242
|
+
# we will match those commands that require identifiers (NAME/ID)
|
|
243
|
+
# e.g. ASSEMBLY-NAME/ID list ... => MATCH
|
|
244
|
+
# e.g. [ASSEMBLY-NAME/ID] list ... => MATCH
|
|
245
|
+
matched_data = task[1].usage.match(/\[?#{command}.?(NAME\/ID|ID\/NAME)\]?/)
|
|
246
|
+
matched_alt_identifiers_data = nil
|
|
247
|
+
|
|
248
|
+
# we chance alternate providers
|
|
249
|
+
if respond_to?(:alternate_identifiers)
|
|
250
|
+
alternate_identifiers = self.alternate_identifiers()
|
|
251
|
+
|
|
252
|
+
alternate_identifiers.each do |a_provider|
|
|
253
|
+
if matched_alt_identifiers_data = task[1].usage.match(/\[?#{a_provider}.?(NAME\/ID|ID\/NAME)\]?/)
|
|
254
|
+
command_alt_sym = "#{command}_#{a_provider}".downcase.to_sym
|
|
255
|
+
cached_tasks[command_alt_sym] = cached_tasks.fetch(command_alt_sym,[])
|
|
256
|
+
cached_tasks[command_alt_sym] << task_name
|
|
257
|
+
# when found break
|
|
258
|
+
break
|
|
259
|
+
end
|
|
260
|
+
end
|
|
261
|
+
end
|
|
262
|
+
|
|
263
|
+
# only if not matched alt data found continue with caching of task
|
|
264
|
+
unless matched_alt_identifiers_data
|
|
265
|
+
if matched_data.nil?
|
|
266
|
+
# no match it means it is tier 1 task, tier 1 => dtk:\assembly>
|
|
267
|
+
# using HIDE_FROM_BASE_CONTEXT to hide command from base context (e.g from dtk:/assembly>) ...
|
|
268
|
+
# ... but to be able to use that command in other context
|
|
269
|
+
# (e.g get-netstats removed from dtk:/assembly> but used in dtk:/assembly/assembly_id/utils)
|
|
270
|
+
cached_tasks[command_sym] << task_name unless usage.include?(HIDE_FROM_BASE_CONTEXT)
|
|
271
|
+
else
|
|
272
|
+
# match means it is tier 2 taks, tier 2 => dtk:\assembly\231312345>
|
|
273
|
+
cached_tasks[command_id_sym] << task_name
|
|
274
|
+
# if there are '[' it means it is optinal identifiers so it is tier 1 and tier 2 task
|
|
275
|
+
cached_tasks[command_sym] << task_name if matched_data[0].include?('[')
|
|
276
|
+
end
|
|
277
|
+
|
|
278
|
+
# n-level matching
|
|
279
|
+
all_children.each do |child|
|
|
280
|
+
current_children = []
|
|
281
|
+
|
|
282
|
+
child.each do |c|
|
|
283
|
+
current_children << c.to_s
|
|
284
|
+
|
|
285
|
+
# create entry e.g. assembly_node_id
|
|
286
|
+
child_id_sym = (command.downcase + '_' + current_children.join('_') + '_wid').to_sym
|
|
287
|
+
|
|
288
|
+
# n-context matching
|
|
289
|
+
matched_data = task[1].usage.match(/^\[?#{c.to_s.upcase}.?(NAME\/ID|ID\/NAME|ID|NAME)(\-?PATTERN)?\]?/)
|
|
290
|
+
if matched_data
|
|
291
|
+
cached_tasks[child_id_sym] = cached_tasks.fetch(child_id_sym,[]) << task_name
|
|
292
|
+
end
|
|
293
|
+
|
|
294
|
+
# override method list, we add these methods only once
|
|
295
|
+
if override_task_obj && !override_task_obj.is_completed?(c)
|
|
296
|
+
command_o_tasks, identifier_o_tasks = override_task_obj.get_all_tasks(c)
|
|
297
|
+
child_sym = (command.downcase + '_' + current_children.join('_')).to_sym
|
|
298
|
+
|
|
299
|
+
command_o_tasks.each do |o_task|
|
|
300
|
+
cached_tasks[child_sym] = cached_tasks.fetch(child_sym,[]) << o_task[0]
|
|
301
|
+
end
|
|
302
|
+
|
|
303
|
+
identifier_o_tasks.each do |o_task|
|
|
304
|
+
cached_tasks[child_id_sym] = cached_tasks.fetch(child_id_sym,[]) << o_task[0]
|
|
305
|
+
end
|
|
306
|
+
|
|
307
|
+
override_task_obj.add_to_completed(c)
|
|
308
|
+
end
|
|
309
|
+
end
|
|
310
|
+
end
|
|
311
|
+
end
|
|
312
|
+
end
|
|
313
|
+
|
|
314
|
+
# there is always help, and in all cases this is exception to the rule
|
|
315
|
+
cached_tasks[command_id_sym] << 'help'
|
|
316
|
+
|
|
317
|
+
return cached_tasks
|
|
318
|
+
end
|
|
319
|
+
|
|
320
|
+
# we make valid methods to make sure that when context changing
|
|
321
|
+
# we allow change only for valid ID/NAME
|
|
322
|
+
def self.valid_id?(value, conn, context_params)
|
|
323
|
+
|
|
324
|
+
context_list = self.get_identifiers(conn, context_params)
|
|
325
|
+
results = context_list.select { |e| e[:name].eql?(value) || e[:identifier].eql?(value.to_i)}
|
|
326
|
+
|
|
327
|
+
return results.empty? ? nil : results.first
|
|
328
|
+
end
|
|
329
|
+
|
|
330
|
+
def self.get_identifiers(conn, context_params)
|
|
331
|
+
@conn = conn if @conn.nil?
|
|
332
|
+
|
|
333
|
+
# we force raw output
|
|
334
|
+
# options = Thor::CoreExt::HashWithIndifferentAccess.new({'list' => true})
|
|
335
|
+
|
|
336
|
+
3.downto(1) do
|
|
337
|
+
# get list data from one of the methods
|
|
338
|
+
if respond_to?(:validation_list)
|
|
339
|
+
response = validation_list(context_params)
|
|
340
|
+
else
|
|
341
|
+
clazz, endpoint, opts = whoami()
|
|
342
|
+
response = get_cached_response(clazz, endpoint, opts)
|
|
343
|
+
end
|
|
344
|
+
|
|
345
|
+
unless (response.nil? || response.empty?)
|
|
346
|
+
unless response['data'].nil?
|
|
347
|
+
identifiers = []
|
|
348
|
+
response['data'].each do |element|
|
|
349
|
+
# special flag to filter out data not needed here
|
|
350
|
+
next if element['dtk_context_hidden']
|
|
351
|
+
|
|
352
|
+
identifiers << { :name => element['display_name'], :identifier => element['id'], :shadow_entity => element['dtk_client_type'] }
|
|
353
|
+
end
|
|
354
|
+
return identifiers
|
|
355
|
+
end
|
|
356
|
+
end
|
|
357
|
+
unless response.nil?
|
|
358
|
+
break if response["status"].eql?('ok')
|
|
359
|
+
end
|
|
360
|
+
sleep(1)
|
|
361
|
+
end
|
|
362
|
+
|
|
363
|
+
DtkLogger.instance.warn("[WARNING] We were not able to check cached context, possible errors may occur.")
|
|
364
|
+
return []
|
|
365
|
+
end
|
|
366
|
+
|
|
367
|
+
no_tasks do
|
|
368
|
+
|
|
369
|
+
include CommonOptionDefs::Mixin
|
|
370
|
+
|
|
371
|
+
#
|
|
372
|
+
# Run shell command directly from main, use with CAUTION
|
|
373
|
+
#
|
|
374
|
+
|
|
375
|
+
def run_shell_command(line)
|
|
376
|
+
TOPLEVEL_BINDING.eval('self').execute_shell_command_internal(line)
|
|
377
|
+
end
|
|
378
|
+
|
|
379
|
+
##
|
|
380
|
+
# Block that allows users to specify part of the code which is expected to run for longer duration
|
|
381
|
+
#
|
|
382
|
+
def extended_timeout
|
|
383
|
+
puts "Please wait, this could take a few minutes ..."
|
|
384
|
+
old_timeout = ::DTK::Client::Conn.get_timeout()
|
|
385
|
+
::DTK::Client::Conn.set_timeout(EXTENDED_TIMEOUT)
|
|
386
|
+
result = yield
|
|
387
|
+
::DTK::Client::Conn.set_timeout(old_timeout)
|
|
388
|
+
result
|
|
389
|
+
end
|
|
390
|
+
|
|
391
|
+
# Method not implemented error
|
|
392
|
+
def not_implemented
|
|
393
|
+
raise DTK::Client::DtkError, "Method NOT IMPLEMENTED!"
|
|
394
|
+
end
|
|
395
|
+
|
|
396
|
+
def raise_validation_error_method_usage(method_name)
|
|
397
|
+
usage_text = self.class.all_tasks[method_name][:usage]
|
|
398
|
+
raise DTK::Client::DtkValidationError, "Invalid method usage, use: #{usage_text}"
|
|
399
|
+
end
|
|
400
|
+
|
|
401
|
+
# returns method name and usage
|
|
402
|
+
def current_method_info
|
|
403
|
+
unless @_initializer[2][:current_task]
|
|
404
|
+
raise DTK::Client::DtkError, "You are using development mode, and you have newer version of Thor gem than specified by dtk-client"
|
|
405
|
+
end
|
|
406
|
+
|
|
407
|
+
return @_initializer[2][:current_task].name, @_initializer[2][:current_task].usage
|
|
408
|
+
end
|
|
409
|
+
|
|
410
|
+
# returns names of the arguments, after the method name
|
|
411
|
+
def method_argument_names
|
|
412
|
+
name, usage = current_method_info
|
|
413
|
+
results = usage.split(name.gsub(/_/,'-')).last || ""
|
|
414
|
+
return results.split(' ')
|
|
415
|
+
end
|
|
416
|
+
|
|
417
|
+
#TODO: can make more efficient by having rest call that returns name from id, rather than using 'list path'
|
|
418
|
+
#entity_id can be a name as well as an id
|
|
419
|
+
def get_name_from_id_helper(entity_id, entity_type=nil,list_command_path=nil, subtype=nil)
|
|
420
|
+
return entity_id unless is_numeric_id?(entity_id)
|
|
421
|
+
|
|
422
|
+
entity_id = entity_id.to_i
|
|
423
|
+
if entity_type.nil?
|
|
424
|
+
entity_type,list_command_path,subtype = self.class.whoami()
|
|
425
|
+
end
|
|
426
|
+
|
|
427
|
+
match = nil
|
|
428
|
+
response = self.class.get_cached_response(entity_type,list_command_path,subtype)
|
|
429
|
+
if response and response.ok? and response['data']
|
|
430
|
+
match = response['data'].find{|entity|entity_id == entity['id']}
|
|
431
|
+
end
|
|
432
|
+
unless match
|
|
433
|
+
raise DTK::Client::DtkError, "Not able to resolve entity name, please provide #{entity_type} name."
|
|
434
|
+
end
|
|
435
|
+
match['display_name']
|
|
436
|
+
end
|
|
437
|
+
|
|
438
|
+
def is_numeric_id?(possible_id)
|
|
439
|
+
!possible_id.to_s.match(/^[0-9]+$/).nil?
|
|
440
|
+
end
|
|
441
|
+
|
|
442
|
+
# helper for error messages; prints singular or plural version
|
|
443
|
+
# tem will be of form singural/plural or simple term in which case plural formed by adding 's'
|
|
444
|
+
def plural?(is_plural,term)
|
|
445
|
+
singular_plural = term.split('/')
|
|
446
|
+
if singular_plural.size == 1
|
|
447
|
+
singular_plural << "#{singular_plural[0]}s"
|
|
448
|
+
end
|
|
449
|
+
singular_plural[is_plural ? 1 : 0]
|
|
450
|
+
end
|
|
451
|
+
|
|
452
|
+
# removes nil values
|
|
453
|
+
def post_body(hash)
|
|
454
|
+
hash.inject(Hash.new){|h,(k,v)|v.nil? ? h : h.merge(k => v)}
|
|
455
|
+
end
|
|
456
|
+
|
|
457
|
+
# User input prompt
|
|
458
|
+
def user_input(message)
|
|
459
|
+
trap("INT", "SIG_IGN")
|
|
460
|
+
while line = Readline.readline("#{message}: ",true)
|
|
461
|
+
unless line.chomp.empty?
|
|
462
|
+
trap("INT", false)
|
|
463
|
+
return line
|
|
464
|
+
end
|
|
465
|
+
end
|
|
466
|
+
end
|
|
467
|
+
|
|
468
|
+
def get_type_and_raise_error_if_invalid(about, default_about, type_options)
|
|
469
|
+
about ||= default_about
|
|
470
|
+
raise DTK::Client::DtkError, "Not supported type '#{about}' for list for current context level. Possible type options: #{type_options.join(', ')}" unless type_options.include?(about)
|
|
471
|
+
return about, about[0..-2].to_sym
|
|
472
|
+
end
|
|
473
|
+
|
|
474
|
+
# check for delimiter, if present returns namespace and name for module/service
|
|
475
|
+
# returns: namespace, name
|
|
476
|
+
def get_namespace_and_name(input_remote_name, delimiter)
|
|
477
|
+
if (input_remote_name||'').include?(delimiter)
|
|
478
|
+
input_remote_name.split(delimiter)
|
|
479
|
+
# support ns/name as well as ns:name
|
|
480
|
+
elsif (input_remote_name||'').include?('/')
|
|
481
|
+
input_remote_name.split('/')
|
|
482
|
+
else
|
|
483
|
+
[nil, input_remote_name]
|
|
484
|
+
end
|
|
485
|
+
end
|
|
486
|
+
|
|
487
|
+
def get_namespace_and_name_for_component(component_full_name)
|
|
488
|
+
namespace, name = nil, ''
|
|
489
|
+
|
|
490
|
+
if (component_full_name||'').include?(':')
|
|
491
|
+
match = component_full_name.match(/(^[^:]+):{1}(.*$)/)
|
|
492
|
+
namespace, name = [$1,$2]
|
|
493
|
+
|
|
494
|
+
return [nil, component_full_name] if (name.include?(':') && !name.include?('::'))
|
|
495
|
+
|
|
496
|
+
# to be robust to user putting in ns::x::y which splits to ns=ns name=:x::y
|
|
497
|
+
name.gsub!(/^:/,'')
|
|
498
|
+
|
|
499
|
+
component_full_name = name
|
|
500
|
+
end
|
|
501
|
+
|
|
502
|
+
[namespace, component_full_name]
|
|
503
|
+
end
|
|
504
|
+
end
|
|
505
|
+
|
|
506
|
+
|
|
507
|
+
##
|
|
508
|
+
# This is fix where we wanna exclude basename print when using dtk-shell.
|
|
509
|
+
# Thor has banner method, representing row when help is invoked. As such banner
|
|
510
|
+
# will print out basename first. e.g.
|
|
511
|
+
#
|
|
512
|
+
# dtk-shell assembly list [library|target] # List asssemblies in library or target
|
|
513
|
+
#
|
|
514
|
+
# Basename is derived from name of file, as such can be overriden to serve some other
|
|
515
|
+
# logic.
|
|
516
|
+
#
|
|
517
|
+
def self.basename
|
|
518
|
+
basename = super
|
|
519
|
+
basename = '' if basename == 'dtk-shell'
|
|
520
|
+
return basename
|
|
521
|
+
end
|
|
522
|
+
|
|
523
|
+
#
|
|
524
|
+
# Returns list of invisible contexts with sufix provided (if any)
|
|
525
|
+
#
|
|
526
|
+
|
|
527
|
+
def self.invisible_context_list(sufix = 'identifier')
|
|
528
|
+
self.respond_to?(:invisible_context) ? self.invisible_context.collect { |i| "#{i}-#{sufix}" } : []
|
|
529
|
+
end
|
|
530
|
+
|
|
531
|
+
desc "help [SUBCOMMAND]", "Describes available subcommands or one specific subcommand"
|
|
532
|
+
def help(*args)
|
|
533
|
+
puts # pretty print
|
|
534
|
+
not_dtk_clazz = true
|
|
535
|
+
|
|
536
|
+
if defined?(DTK::Client::Dtk)
|
|
537
|
+
not_dtk_clazz = !self.class.eql?(DTK::Client::Dtk)
|
|
538
|
+
end
|
|
539
|
+
|
|
540
|
+
# not_dtk_clazz - we don't use subcommand print in case of root DTK class
|
|
541
|
+
# for other classes Assembly, Node, etc. we print subcommand
|
|
542
|
+
# this gives us console output: dtk assembly converge ASSEMBLY-ID
|
|
543
|
+
#
|
|
544
|
+
# @@shell_execution - if we run from shell we don't want subcommand output
|
|
545
|
+
#
|
|
546
|
+
|
|
547
|
+
super(args.empty? ? nil : args.first, not_dtk_clazz && !@@shell_execution)
|
|
548
|
+
|
|
549
|
+
# we will print error in case configuration has reported error
|
|
550
|
+
@conn.print_warning if @conn.connection_error?
|
|
551
|
+
puts # pretty print
|
|
552
|
+
end
|
|
553
|
+
end
|
|
554
|
+
end
|
|
555
|
+
end
|