dtk-shell 0.10.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (191) hide show
  1. checksums.yaml +7 -0
  2. data/Gemfile +5 -0
  3. data/Gemfile_dev +13 -0
  4. data/README.md +121 -0
  5. data/bin/dtk-execute +32 -0
  6. data/bin/dtk-run +92 -0
  7. data/bin/dtk-shell +31 -0
  8. data/dtk-shell.gemspec +50 -0
  9. data/lib/auxiliary.rb +61 -0
  10. data/lib/bundler_monkey_patch.rb +26 -0
  11. data/lib/client.rb +58 -0
  12. data/lib/command_helper.rb +33 -0
  13. data/lib/command_helpers/git_repo.rb +589 -0
  14. data/lib/command_helpers/git_repo/merge.rb +153 -0
  15. data/lib/command_helpers/jenkins_client.rb +106 -0
  16. data/lib/command_helpers/jenkins_client/config_xml.rb +288 -0
  17. data/lib/command_helpers/service_importer.rb +251 -0
  18. data/lib/command_helpers/service_link.rb +33 -0
  19. data/lib/command_helpers/test_module_creator.rb +69 -0
  20. data/lib/command_helpers/test_module_templates/dtk.model.yaml.eruby +10 -0
  21. data/lib/command_helpers/test_module_templates/spec_helper.rb.eruby +10 -0
  22. data/lib/command_helpers/test_module_templates/temp_component_spec.rb.eruby +5 -0
  23. data/lib/commands.rb +57 -0
  24. data/lib/commands/common/thor/access_control.rb +133 -0
  25. data/lib/commands/common/thor/action_result_handler.rb +74 -0
  26. data/lib/commands/common/thor/assembly_template.rb +92 -0
  27. data/lib/commands/common/thor/assembly_workspace.rb +1801 -0
  28. data/lib/commands/common/thor/base_command_helper.rb +59 -0
  29. data/lib/commands/common/thor/clone.rb +82 -0
  30. data/lib/commands/common/thor/common.rb +88 -0
  31. data/lib/commands/common/thor/common_base.rb +49 -0
  32. data/lib/commands/common/thor/create_target.rb +70 -0
  33. data/lib/commands/common/thor/edit.rb +255 -0
  34. data/lib/commands/common/thor/inventory_parser.rb +98 -0
  35. data/lib/commands/common/thor/list_diffs.rb +128 -0
  36. data/lib/commands/common/thor/module.rb +1011 -0
  37. data/lib/commands/common/thor/module/import.rb +210 -0
  38. data/lib/commands/common/thor/node.rb +53 -0
  39. data/lib/commands/common/thor/poller.rb +65 -0
  40. data/lib/commands/common/thor/pull_clone_changes.rb +28 -0
  41. data/lib/commands/common/thor/pull_from_remote.rb +152 -0
  42. data/lib/commands/common/thor/puppet_forge.rb +72 -0
  43. data/lib/commands/common/thor/purge_clone.rb +101 -0
  44. data/lib/commands/common/thor/push_clone_changes.rb +162 -0
  45. data/lib/commands/common/thor/push_to_remote.rb +94 -0
  46. data/lib/commands/common/thor/remotes.rb +71 -0
  47. data/lib/commands/common/thor/reparse.rb +40 -0
  48. data/lib/commands/common/thor/set_required_attributes.rb +46 -0
  49. data/lib/commands/thor/account.rb +239 -0
  50. data/lib/commands/thor/assembly.rb +356 -0
  51. data/lib/commands/thor/attribute.rb +79 -0
  52. data/lib/commands/thor/component.rb +70 -0
  53. data/lib/commands/thor/component_module.rb +501 -0
  54. data/lib/commands/thor/component_template.rb +174 -0
  55. data/lib/commands/thor/dependency.rb +34 -0
  56. data/lib/commands/thor/developer.rb +144 -0
  57. data/lib/commands/thor/dtk.rb +152 -0
  58. data/lib/commands/thor/library.rb +125 -0
  59. data/lib/commands/thor/node.rb +504 -0
  60. data/lib/commands/thor/node_template.rb +94 -0
  61. data/lib/commands/thor/project.rb +34 -0
  62. data/lib/commands/thor/provider.rb +233 -0
  63. data/lib/commands/thor/remotes.rb +49 -0
  64. data/lib/commands/thor/service.rb +941 -0
  65. data/lib/commands/thor/service_module.rb +914 -0
  66. data/lib/commands/thor/state_change.rb +25 -0
  67. data/lib/commands/thor/target.rb +250 -0
  68. data/lib/commands/thor/task.rb +116 -0
  69. data/lib/commands/thor/test_module.rb +310 -0
  70. data/lib/commands/thor/utils.rb +21 -0
  71. data/lib/commands/thor/workspace.rb +685 -0
  72. data/lib/config/cacert.pem +3785 -0
  73. data/lib/config/client.conf.header +20 -0
  74. data/lib/config/configuration.rb +99 -0
  75. data/lib/config/default.conf +16 -0
  76. data/lib/config/disk_cacher.rb +80 -0
  77. data/lib/configurator.rb +176 -0
  78. data/lib/context_router.rb +44 -0
  79. data/lib/core.rb +497 -0
  80. data/lib/domain/git_adapter.rb +412 -0
  81. data/lib/domain/git_error_handler.rb +64 -0
  82. data/lib/domain/response.rb +285 -0
  83. data/lib/domain/response/error_handler.rb +86 -0
  84. data/lib/dtk-shell/version.rb +20 -0
  85. data/lib/dtk_constants.rb +40 -0
  86. data/lib/dtk_error.rb +114 -0
  87. data/lib/dtk_logger.rb +126 -0
  88. data/lib/dtk_shell.rb +31 -0
  89. data/lib/error.rb +85 -0
  90. data/lib/execute.rb +29 -0
  91. data/lib/execute/cli_pure/cli_rerouter.rb +102 -0
  92. data/lib/execute/command.rb +40 -0
  93. data/lib/execute/command/api_call.rb +60 -0
  94. data/lib/execute/command/api_call/map.rb +60 -0
  95. data/lib/execute/command/api_call/service.rb +91 -0
  96. data/lib/execute/command/api_call/translation_term.rb +119 -0
  97. data/lib/execute/command/rest_call.rb +37 -0
  98. data/lib/execute/command_processor.rb +30 -0
  99. data/lib/execute/command_processor/rest_call.rb +59 -0
  100. data/lib/execute/error_usage.rb +21 -0
  101. data/lib/execute/execute_context.rb +86 -0
  102. data/lib/execute/execute_context/result_store.rb +37 -0
  103. data/lib/execute/script.rb +64 -0
  104. data/lib/execute/script/add_tenant.rb +121 -0
  105. data/lib/git-logs/git.log +0 -0
  106. data/lib/parser/adapters/option_parser.rb +70 -0
  107. data/lib/parser/adapters/thor.rb +555 -0
  108. data/lib/parser/adapters/thor/common_option_defs.rb +40 -0
  109. data/lib/require_first.rb +104 -0
  110. data/lib/search_hash.rb +44 -0
  111. data/lib/shell.rb +261 -0
  112. data/lib/shell/context.rb +1065 -0
  113. data/lib/shell/context_aux.rb +46 -0
  114. data/lib/shell/domain/active_context.rb +186 -0
  115. data/lib/shell/domain/context_entity.rb +89 -0
  116. data/lib/shell/domain/context_params.rb +223 -0
  117. data/lib/shell/domain/override_tasks.rb +88 -0
  118. data/lib/shell/domain/shadow_entity.rb +76 -0
  119. data/lib/shell/header_shell.rb +44 -0
  120. data/lib/shell/help_monkey_patch.rb +283 -0
  121. data/lib/shell/interactive_wizard.rb +225 -0
  122. data/lib/shell/message_queue.rb +63 -0
  123. data/lib/shell/parse_monkey_patch.rb +39 -0
  124. data/lib/shell/status_monitor.rb +124 -0
  125. data/lib/task_status.rb +83 -0
  126. data/lib/task_status/refresh_mode.rb +77 -0
  127. data/lib/task_status/snapshot_mode.rb +28 -0
  128. data/lib/task_status/stream_mode.rb +48 -0
  129. data/lib/task_status/stream_mode/element.rb +101 -0
  130. data/lib/task_status/stream_mode/element/format.rb +101 -0
  131. data/lib/task_status/stream_mode/element/hierarchical_task.rb +100 -0
  132. data/lib/task_status/stream_mode/element/hierarchical_task/result.rb +72 -0
  133. data/lib/task_status/stream_mode/element/hierarchical_task/result/action.rb +93 -0
  134. data/lib/task_status/stream_mode/element/hierarchical_task/result/components.rb +26 -0
  135. data/lib/task_status/stream_mode/element/hierarchical_task/result/node_level.rb +26 -0
  136. data/lib/task_status/stream_mode/element/hierarchical_task/steps.rb +34 -0
  137. data/lib/task_status/stream_mode/element/hierarchical_task/steps/action.rb +53 -0
  138. data/lib/task_status/stream_mode/element/hierarchical_task/steps/components.rb +53 -0
  139. data/lib/task_status/stream_mode/element/hierarchical_task/steps/node_level.rb +42 -0
  140. data/lib/task_status/stream_mode/element/no_results.rb +26 -0
  141. data/lib/task_status/stream_mode/element/render.rb +59 -0
  142. data/lib/task_status/stream_mode/element/stage.rb +84 -0
  143. data/lib/task_status/stream_mode/element/stage/render.rb +76 -0
  144. data/lib/task_status/stream_mode/element/task_end.rb +35 -0
  145. data/lib/task_status/stream_mode/element/task_start.rb +37 -0
  146. data/lib/util/common_util.rb +37 -0
  147. data/lib/util/console.rb +235 -0
  148. data/lib/util/dtk_puppet.rb +65 -0
  149. data/lib/util/module_util.rb +66 -0
  150. data/lib/util/os_util.rb +385 -0
  151. data/lib/util/permission_util.rb +31 -0
  152. data/lib/util/remote_dependency_util.rb +84 -0
  153. data/lib/util/ssh_util.rb +94 -0
  154. data/lib/view_processor.rb +129 -0
  155. data/lib/view_processor/augmented_simple_list.rb +44 -0
  156. data/lib/view_processor/hash_pretty_print.rb +123 -0
  157. data/lib/view_processor/simple_list.rb +156 -0
  158. data/lib/view_processor/table_print.rb +309 -0
  159. data/lib/violation.rb +86 -0
  160. data/lib/violation/attribute.rb +76 -0
  161. data/lib/violation/fix.rb +26 -0
  162. data/lib/violation/fix/result.rb +73 -0
  163. data/lib/violation/fix/result/error.rb +34 -0
  164. data/lib/violation/fix/set_attribute.rb +41 -0
  165. data/lib/violation/sub_classes.rb +60 -0
  166. data/puppet/manifests/init.pp +72 -0
  167. data/puppet/manifests/params.pp +16 -0
  168. data/puppet/r8meta.puppet.yml +35 -0
  169. data/puppet/templates/bash_profile.erb +2 -0
  170. data/puppet/templates/client.conf.erb +1 -0
  171. data/puppet/templates/dtkclient.erb +2 -0
  172. data/spec/component_module_spec.rb +34 -0
  173. data/spec/dependency_spec.rb +6 -0
  174. data/spec/dtk_shell_spec.rb +13 -0
  175. data/spec/dtk_spec.rb +33 -0
  176. data/spec/lib/spec_helper.rb +10 -0
  177. data/spec/lib/spec_thor.rb +108 -0
  178. data/spec/node_template_spec.rb +24 -0
  179. data/spec/project_spec.rb +6 -0
  180. data/spec/repo_spec.rb +7 -0
  181. data/spec/response_spec.rb +52 -0
  182. data/spec/service_module_spec.rb +38 -0
  183. data/spec/service_spec.rb +50 -0
  184. data/spec/state_change_spec.rb +7 -0
  185. data/spec/table_print_spec.rb +48 -0
  186. data/spec/target_spec.rb +57 -0
  187. data/spec/task_spec.rb +28 -0
  188. data/views/assembly/augmented_simple_list.rb +12 -0
  189. data/views/assembly_template/augmented_simple_list.rb +12 -0
  190. data/views/list_task/augmented_simple_list.rb +12 -0
  191. 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