haveapi-fs 0.1.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/.yardopts +1 -0
- data/CHANGELOG +2 -0
- data/Gemfile +3 -0
- data/LICENSE.txt +21 -0
- data/README.md +338 -0
- data/Rakefile +1 -0
- data/assets/css/bootstrap.min.css +6 -0
- data/assets/css/local.css +11 -0
- data/bin/haveapi-fs +5 -0
- data/haveapi-fs.gemspec +30 -0
- data/lib/core_ext/string.rb +9 -0
- data/lib/haveapi/fs/auth/base.rb +56 -0
- data/lib/haveapi/fs/auth/basic.rb +29 -0
- data/lib/haveapi/fs/auth/noauth.rb +9 -0
- data/lib/haveapi/fs/auth/token.rb +39 -0
- data/lib/haveapi/fs/cache.rb +71 -0
- data/lib/haveapi/fs/cleaner.rb +56 -0
- data/lib/haveapi/fs/component.rb +237 -0
- data/lib/haveapi/fs/components/action_dir.rb +106 -0
- data/lib/haveapi/fs/components/action_errors.rb +49 -0
- data/lib/haveapi/fs/components/action_exec.rb +12 -0
- data/lib/haveapi/fs/components/action_exec_edit.rb +90 -0
- data/lib/haveapi/fs/components/action_input.rb +40 -0
- data/lib/haveapi/fs/components/action_message.rb +19 -0
- data/lib/haveapi/fs/components/action_output.rb +79 -0
- data/lib/haveapi/fs/components/action_status.rb +32 -0
- data/lib/haveapi/fs/components/cache_stats.rb +19 -0
- data/lib/haveapi/fs/components/component_list.rb +24 -0
- data/lib/haveapi/fs/components/create_action_dir.rb +15 -0
- data/lib/haveapi/fs/components/delete_action_dir.rb +22 -0
- data/lib/haveapi/fs/components/directory.rb +45 -0
- data/lib/haveapi/fs/components/directory_reset.rb +8 -0
- data/lib/haveapi/fs/components/executable.rb +75 -0
- data/lib/haveapi/fs/components/file.rb +43 -0
- data/lib/haveapi/fs/components/groff_help_file.rb +9 -0
- data/lib/haveapi/fs/components/help_file.rb +28 -0
- data/lib/haveapi/fs/components/html_help_file.rb +24 -0
- data/lib/haveapi/fs/components/index_filter.rb +63 -0
- data/lib/haveapi/fs/components/info_files.rb +19 -0
- data/lib/haveapi/fs/components/instance_create.rb +20 -0
- data/lib/haveapi/fs/components/instance_edit.rb +49 -0
- data/lib/haveapi/fs/components/list_item.rb +28 -0
- data/lib/haveapi/fs/components/md_help_file.rb +24 -0
- data/lib/haveapi/fs/components/meta_dir.rb +42 -0
- data/lib/haveapi/fs/components/meta_file.rb +21 -0
- data/lib/haveapi/fs/components/parameter.rb +132 -0
- data/lib/haveapi/fs/components/pry.rb +9 -0
- data/lib/haveapi/fs/components/remote_control_file.rb +92 -0
- data/lib/haveapi/fs/components/resource_action_dir.rb +72 -0
- data/lib/haveapi/fs/components/resource_dir.rb +161 -0
- data/lib/haveapi/fs/components/resource_id.rb +15 -0
- data/lib/haveapi/fs/components/resource_instance_dir.rb +146 -0
- data/lib/haveapi/fs/components/rfuse_check.rb +3 -0
- data/lib/haveapi/fs/components/root.rb +75 -0
- data/lib/haveapi/fs/components/save_instance.rb +11 -0
- data/lib/haveapi/fs/components/unsaved_list.rb +24 -0
- data/lib/haveapi/fs/components/update_action_dir.rb +31 -0
- data/lib/haveapi/fs/context.rb +43 -0
- data/lib/haveapi/fs/exceptions.rb +0 -0
- data/lib/haveapi/fs/factory.rb +59 -0
- data/lib/haveapi/fs/fs.rb +198 -0
- data/lib/haveapi/fs/help.rb +91 -0
- data/lib/haveapi/fs/main.rb +134 -0
- data/lib/haveapi/fs/remote_control.rb +29 -0
- data/lib/haveapi/fs/version.rb +5 -0
- data/lib/haveapi/fs/worker.rb +77 -0
- data/lib/haveapi/fs.rb +65 -0
- data/templates/help/html/action_dir.erb +33 -0
- data/templates/help/html/action_errors.erb +4 -0
- data/templates/help/html/action_input.erb +40 -0
- data/templates/help/html/action_output.erb +21 -0
- data/templates/help/html/index_filter.erb +16 -0
- data/templates/help/html/layout.erb +45 -0
- data/templates/help/html/resource_action_dir.erb +18 -0
- data/templates/help/html/resource_dir.erb +18 -0
- data/templates/help/html/resource_instance_dir.erb +64 -0
- data/templates/help/html/root.erb +42 -0
- data/templates/help/md/action_dir.erb +29 -0
- data/templates/help/md/action_errors.erb +2 -0
- data/templates/help/md/action_input.erb +23 -0
- data/templates/help/md/action_output.erb +11 -0
- data/templates/help/md/index_filter.erb +11 -0
- data/templates/help/md/layout.erb +14 -0
- data/templates/help/md/resource_action_dir.erb +10 -0
- data/templates/help/md/resource_dir.erb +15 -0
- data/templates/help/md/resource_instance_dir.erb +42 -0
- data/templates/help/md/root.erb +34 -0
- metadata +231 -0
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
module HaveAPI::Fs
|
|
2
|
+
# All built-in components are stored in this module.
|
|
3
|
+
module Components ; end
|
|
4
|
+
|
|
5
|
+
# The basic building block of the file system. Every directory and file is
|
|
6
|
+
# represented by a subclass of this class.
|
|
7
|
+
class Component
|
|
8
|
+
# An encapsulation of a Hash to store child components.
|
|
9
|
+
class Children
|
|
10
|
+
attr_accessor :context
|
|
11
|
+
|
|
12
|
+
# @param [HaveAPI::Fs::Context] ctx
|
|
13
|
+
def initialize(ctx)
|
|
14
|
+
@context = ctx
|
|
15
|
+
@store = {}
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def [](k)
|
|
19
|
+
@store[k]
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
# Replace a child named `k` by a new child represented by `v`. The old
|
|
23
|
+
# child, if present, is invalidated and dropped from the cache.
|
|
24
|
+
# {Factory} is used to create an instance of `v`.
|
|
25
|
+
#
|
|
26
|
+
# @param [Symbol] k
|
|
27
|
+
# @param [Array] v
|
|
28
|
+
def []=(k, v)
|
|
29
|
+
if @store.has_key?(k)
|
|
30
|
+
@store[k].invalidate
|
|
31
|
+
@store[k].context.cache.drop_below(@store[k].path)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
@store[k] = Factory.create(@context, k, *v)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def set(k, v)
|
|
38
|
+
@store[k] = v
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
%i(has_key? clear each select detect delete_if).each do |m|
|
|
42
|
+
define_method(m) do |*args, &block|
|
|
43
|
+
@store.send(m, *args, &block)
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
class << self
|
|
49
|
+
# Define reader methods for child components.
|
|
50
|
+
def children_reader(*args)
|
|
51
|
+
args.each do |arg|
|
|
52
|
+
define_method(arg) { children[arg] }
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
# Set or get a component name. Component name is used for finding
|
|
57
|
+
# components within a {Context}.
|
|
58
|
+
#
|
|
59
|
+
# @param [Symbol] name
|
|
60
|
+
# @return [nil] if name is set
|
|
61
|
+
# @return [Symbol] if name is nil
|
|
62
|
+
def component(name = nil)
|
|
63
|
+
if name
|
|
64
|
+
@component = name
|
|
65
|
+
|
|
66
|
+
else
|
|
67
|
+
@component
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
# Pass component name to the subclass.
|
|
72
|
+
def inherited(subclass)
|
|
73
|
+
subclass.component(@component)
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
attr_accessor :context, :atime, :mtime, :ctime
|
|
78
|
+
|
|
79
|
+
# @param [Boolean] bound
|
|
80
|
+
def initialize(bound: false)
|
|
81
|
+
@bound = bound
|
|
82
|
+
@atime = @mtime = @ctime = Time.now
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
# Called by {Factory} when the instance is prepared. Subclasses must call
|
|
86
|
+
# this method.
|
|
87
|
+
def setup
|
|
88
|
+
@children = Children.new(context)
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
# Attempt to find a child component with `name`.
|
|
92
|
+
#
|
|
93
|
+
# @return [HaveAPI::Fs::Component] if found
|
|
94
|
+
# @return [nil] if not found
|
|
95
|
+
def find(name)
|
|
96
|
+
return @children[name] if @children.has_key?(name)
|
|
97
|
+
c = new_child(name)
|
|
98
|
+
|
|
99
|
+
@children.set(name, Factory.create(context, name, *c)) if c
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
# Attempt to find and use nested components with `names`. Each name is for
|
|
103
|
+
# the next descendant. If the target component is found, it and all
|
|
104
|
+
# components in its path will be bound. Bound components are not
|
|
105
|
+
# automatically deleted when not in use.
|
|
106
|
+
def use(*names)
|
|
107
|
+
ret = self
|
|
108
|
+
path = []
|
|
109
|
+
|
|
110
|
+
names.each do |n|
|
|
111
|
+
ret = ret.find(n)
|
|
112
|
+
return if ret.nil?
|
|
113
|
+
path << ret
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
path.each { |c| c.bound = true }
|
|
117
|
+
|
|
118
|
+
ret
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
def bound?
|
|
122
|
+
@bound
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
def bound=(b)
|
|
126
|
+
@bound = b
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
def directory?
|
|
130
|
+
!file?
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
def file?
|
|
134
|
+
!directory?
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
def readable?
|
|
138
|
+
true
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
def writable?
|
|
142
|
+
false
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
def executable?
|
|
146
|
+
false
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
def contents
|
|
150
|
+
raise NotImplementedError
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
def times
|
|
154
|
+
[@atime, @mtime, @ctime]
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
# Shortcut for {#drop_children} and {#setup}.
|
|
158
|
+
def reset
|
|
159
|
+
drop_children
|
|
160
|
+
setup
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
def title
|
|
164
|
+
self.class.name
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
# @return [String] path of this component in the tree without the leading /
|
|
168
|
+
def path
|
|
169
|
+
context.file_path.join('/')
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
# @return [String] absolute path of this component from the system root
|
|
173
|
+
def abspath
|
|
174
|
+
File.join(
|
|
175
|
+
context.mountpoint,
|
|
176
|
+
path
|
|
177
|
+
)
|
|
178
|
+
end
|
|
179
|
+
|
|
180
|
+
def parent
|
|
181
|
+
context.object_path[-2][1]
|
|
182
|
+
end
|
|
183
|
+
|
|
184
|
+
# A component is unsaved if it or any of its descendants has been modified
|
|
185
|
+
# and not saved.
|
|
186
|
+
#
|
|
187
|
+
# @param [Integer] n used to determine the result just once per the same `n`
|
|
188
|
+
# @return [Boolean]
|
|
189
|
+
def unsaved?(n = nil)
|
|
190
|
+
return @is_unsaved if n && @last_unsaved == n
|
|
191
|
+
|
|
192
|
+
child = @children.detect { |_, c| c.unsaved? }
|
|
193
|
+
|
|
194
|
+
@last_unsaved = n
|
|
195
|
+
@is_unsaved = !child.nil?
|
|
196
|
+
end
|
|
197
|
+
|
|
198
|
+
# Mark the component and all its descendats as invalid. Invalid components
|
|
199
|
+
# can still be in the cache and are dropped on hit.
|
|
200
|
+
def invalidate
|
|
201
|
+
@invalid = true
|
|
202
|
+
|
|
203
|
+
children.each { |_, c| c.invalidate }
|
|
204
|
+
end
|
|
205
|
+
|
|
206
|
+
def invalid?
|
|
207
|
+
@invalid
|
|
208
|
+
end
|
|
209
|
+
|
|
210
|
+
protected
|
|
211
|
+
attr_accessor :children
|
|
212
|
+
|
|
213
|
+
# Called to create a component for a child with `name` if this child is not
|
|
214
|
+
# yet or not anymore in memory. All subclasses should extend this method to
|
|
215
|
+
# add their own custom contents.
|
|
216
|
+
#
|
|
217
|
+
# @param [Symbol] name
|
|
218
|
+
# @return [Array] the array describes the new child to be created by
|
|
219
|
+
# {Factory}. The first item is a class name and
|
|
220
|
+
# the rest are arguments to its constructor.
|
|
221
|
+
# @return [nil] if the child does not exist
|
|
222
|
+
def new_child(name)
|
|
223
|
+
raise NotImplementedError
|
|
224
|
+
end
|
|
225
|
+
|
|
226
|
+
# Drop all children from the memory and clear them from the cache.
|
|
227
|
+
def drop_children
|
|
228
|
+
@children.clear
|
|
229
|
+
context.cache.drop_below(path)
|
|
230
|
+
end
|
|
231
|
+
|
|
232
|
+
# Update the time of last modification.
|
|
233
|
+
def changed
|
|
234
|
+
self.mtime = Time.now
|
|
235
|
+
end
|
|
236
|
+
end
|
|
237
|
+
end
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
module HaveAPI::Fs::Components
|
|
2
|
+
class ActionDir < Directory
|
|
3
|
+
component :action_dir
|
|
4
|
+
attr_reader :resource, :action
|
|
5
|
+
children_reader :status, :input, :output
|
|
6
|
+
|
|
7
|
+
def initialize(resource, action)
|
|
8
|
+
super()
|
|
9
|
+
|
|
10
|
+
@resource = resource
|
|
11
|
+
@action = action
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def setup
|
|
15
|
+
super
|
|
16
|
+
|
|
17
|
+
children[:status] = [ActionStatus, self, bound: true]
|
|
18
|
+
children[:message] = [ActionMessage, self, bound: true]
|
|
19
|
+
children[:errors] = [ActionErrors, self, bound: true]
|
|
20
|
+
children[:input] = [ActionInput, self, bound: true]
|
|
21
|
+
children[:output] = [ActionOutput, self, bound: true]
|
|
22
|
+
children[:exec] = [ActionExec, self, bound: true]
|
|
23
|
+
children[:reset] = [DirectoryReset, bound: true]
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def contents
|
|
27
|
+
ret = super + %w(input output status message errors exec reset)
|
|
28
|
+
ret << 'exec.yml' if @action.input_params.any?
|
|
29
|
+
ret
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def exec(meta: {})
|
|
33
|
+
@action.provide_args(*@resource.prepared_args)
|
|
34
|
+
ret = HaveAPI::Client::Response.new(
|
|
35
|
+
@action,
|
|
36
|
+
@action.execute(
|
|
37
|
+
children[:input].values.update({meta: meta})
|
|
38
|
+
)
|
|
39
|
+
)
|
|
40
|
+
|
|
41
|
+
children[:status].set(ret.ok?)
|
|
42
|
+
|
|
43
|
+
if ret.ok?
|
|
44
|
+
case @action.output_layout
|
|
45
|
+
when :object
|
|
46
|
+
res = HaveAPI::Client::ResourceInstance.new(
|
|
47
|
+
@resource.instance_variable_get('@client'),
|
|
48
|
+
@resource.instance_variable_get('@api'),
|
|
49
|
+
@resource,
|
|
50
|
+
action: @action,
|
|
51
|
+
response: ret,
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
when :object_list
|
|
55
|
+
res = HaveAPI::Client::ResourceInstanceList.new(
|
|
56
|
+
@resource.instance_variable_get('@client'),
|
|
57
|
+
@resource.instance_variable_get('@api'),
|
|
58
|
+
@resource,
|
|
59
|
+
@action,
|
|
60
|
+
ret,
|
|
61
|
+
)
|
|
62
|
+
|
|
63
|
+
else
|
|
64
|
+
res = ret
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
children[:output].data = res
|
|
68
|
+
|
|
69
|
+
else
|
|
70
|
+
children[:message].set(ret.message)
|
|
71
|
+
children[:errors].set(ret.errors)
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
ret
|
|
75
|
+
|
|
76
|
+
rescue HaveAPI::Client::ValidationError => e
|
|
77
|
+
children[:status].set(false)
|
|
78
|
+
children[:message].set(e.message)
|
|
79
|
+
children[:errors].set(e.errors)
|
|
80
|
+
e
|
|
81
|
+
|
|
82
|
+
rescue HaveAPI::Client::ActionFailed => e
|
|
83
|
+
children[:status].set(false)
|
|
84
|
+
children[:message].set(e.response.message)
|
|
85
|
+
children[:errors].set(e.response.errors)
|
|
86
|
+
e.response
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
def title
|
|
90
|
+
@action.name.capitalize
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
protected
|
|
94
|
+
def new_child(name)
|
|
95
|
+
if child = super
|
|
96
|
+
child
|
|
97
|
+
|
|
98
|
+
elsif name == :'exec.yml' && @action.input_params.any?
|
|
99
|
+
[ActionExecEdit, self]
|
|
100
|
+
|
|
101
|
+
else
|
|
102
|
+
nil
|
|
103
|
+
end
|
|
104
|
+
end
|
|
105
|
+
end
|
|
106
|
+
end
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
module HaveAPI::Fs::Components
|
|
2
|
+
class ActionErrors < Directory
|
|
3
|
+
component :action_errors
|
|
4
|
+
|
|
5
|
+
class ActionError < File
|
|
6
|
+
def initialize(errors)
|
|
7
|
+
@errors = errors
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def read
|
|
11
|
+
@errors.join("\n") + "\n"
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def initialize(action_dir, *args)
|
|
16
|
+
super(*args)
|
|
17
|
+
@action_dir = action_dir
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def contents
|
|
21
|
+
ret = super
|
|
22
|
+
return ret unless @errors
|
|
23
|
+
ret.concat(@errors.keys.map(&:to_s))
|
|
24
|
+
ret
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def set(errors)
|
|
28
|
+
changed
|
|
29
|
+
@errors = errors
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def title
|
|
33
|
+
'Errors'
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
protected
|
|
37
|
+
def new_child(name)
|
|
38
|
+
if child = super
|
|
39
|
+
child
|
|
40
|
+
|
|
41
|
+
elsif @errors && @errors.has_key?(name)
|
|
42
|
+
[ActionError, @errors[name]]
|
|
43
|
+
|
|
44
|
+
else
|
|
45
|
+
nil
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
require 'yaml'
|
|
2
|
+
|
|
3
|
+
module HaveAPI::Fs::Components
|
|
4
|
+
class ActionExecEdit < File
|
|
5
|
+
def initialize(action_dir)
|
|
6
|
+
super()
|
|
7
|
+
@action_dir = action_dir
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def writable?
|
|
11
|
+
true
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def read
|
|
15
|
+
ret = header + "\n"
|
|
16
|
+
|
|
17
|
+
@action_dir.action.input_params.each do |name, p|
|
|
18
|
+
param_file = @action_dir.find(:input).find(name)
|
|
19
|
+
|
|
20
|
+
if param_file.set?
|
|
21
|
+
v = param_file.new_value
|
|
22
|
+
|
|
23
|
+
elsif p[:default].nil?
|
|
24
|
+
v = nil
|
|
25
|
+
|
|
26
|
+
else
|
|
27
|
+
v = p[:default]
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
ret += "# #{p[:label]}; #{p[:type]}\n"
|
|
31
|
+
ret += "# #{p[:description]}\n"
|
|
32
|
+
ret += "# Defaults to '#{p[:default]}'\n" unless p[:default].nil?
|
|
33
|
+
|
|
34
|
+
if p[:required] || param_file.set?
|
|
35
|
+
ret += "#{name}: #{v}"
|
|
36
|
+
|
|
37
|
+
else
|
|
38
|
+
ret += "##{name}: #{v}"
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
ret += "\n\n"
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
ret
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def write(str)
|
|
48
|
+
return if str.strip.empty?
|
|
49
|
+
|
|
50
|
+
data = YAML.load(str)
|
|
51
|
+
raise Errno::EIO, 'invalid yaml document' unless data.is_a?(::Hash)
|
|
52
|
+
return unless save?(data)
|
|
53
|
+
|
|
54
|
+
params = @action_dir.action.input_params
|
|
55
|
+
|
|
56
|
+
data.each do |k, v|
|
|
57
|
+
p = @action_dir.find(:input).find(k.to_sym)
|
|
58
|
+
next if p.nil?
|
|
59
|
+
|
|
60
|
+
# Type coercion is done later by the client during action call
|
|
61
|
+
p.write_safe(v)
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
save
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def header
|
|
68
|
+
<<END
|
|
69
|
+
# This file is in YAML format. Lines beginning with a hash (#) are comments and
|
|
70
|
+
# are ignored. The action will be executed once this file is saved and closed.
|
|
71
|
+
# The success of this operation can be later checked in
|
|
72
|
+
# actions/#{@action_dir.action.name}/status.
|
|
73
|
+
#
|
|
74
|
+
# Only required parameters that need to be set are uncommented by default.
|
|
75
|
+
# Parameters that are not specified when this file is closed will not be sent
|
|
76
|
+
# to the API.
|
|
77
|
+
#
|
|
78
|
+
# To cancel the operation, either do not save the file or save it empty.
|
|
79
|
+
END
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def save?(data)
|
|
83
|
+
true
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def save
|
|
87
|
+
@action_dir.exec
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
end
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
module HaveAPI::Fs::Components
|
|
2
|
+
class ActionInput < Directory
|
|
3
|
+
component :action_input
|
|
4
|
+
attr_reader :action_dir
|
|
5
|
+
|
|
6
|
+
def initialize(action_dir, *args)
|
|
7
|
+
super(*args)
|
|
8
|
+
@action_dir = action_dir
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def contents
|
|
12
|
+
super + parameters.keys.map(&:to_s)
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def parameters
|
|
16
|
+
@action_dir.action.input_params
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def values
|
|
20
|
+
Hash[children.select { |n, c| c.is_a?(Parameter) && c.set? }.map { |n, c| [n, c.value] }]
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def title
|
|
24
|
+
'Input parameters'
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
protected
|
|
28
|
+
def new_child(name)
|
|
29
|
+
if child = super
|
|
30
|
+
child
|
|
31
|
+
|
|
32
|
+
elsif @action_dir.action.input_params.has_key?(name)
|
|
33
|
+
[Parameter, @action_dir.action, name, :input]
|
|
34
|
+
|
|
35
|
+
else
|
|
36
|
+
nil
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
module HaveAPI::Fs::Components
|
|
2
|
+
class ActionMessage < File
|
|
3
|
+
def initialize(action_dir, *args)
|
|
4
|
+
super(*args)
|
|
5
|
+
@action_dir = action_dir
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
def read
|
|
9
|
+
ret = @msg.to_s
|
|
10
|
+
ret += "\n" unless ret.empty?
|
|
11
|
+
ret
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def set(msg)
|
|
15
|
+
changed
|
|
16
|
+
@msg = msg
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
module HaveAPI::Fs::Components
|
|
2
|
+
class ActionOutput < Directory
|
|
3
|
+
component :action_output
|
|
4
|
+
attr_reader :action_dir
|
|
5
|
+
attr_accessor :data
|
|
6
|
+
|
|
7
|
+
def initialize(action_dir, *args)
|
|
8
|
+
super(*args)
|
|
9
|
+
|
|
10
|
+
@action_dir = action_dir
|
|
11
|
+
|
|
12
|
+
if %i(hash_list object_list).include?(@action_dir.action.output_layout.to_sym)
|
|
13
|
+
@list = true
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def contents
|
|
18
|
+
ret = super
|
|
19
|
+
|
|
20
|
+
return ret unless @data
|
|
21
|
+
|
|
22
|
+
if @list
|
|
23
|
+
if @data.is_a?(HaveAPI::Client::ResourceInstanceList)
|
|
24
|
+
ret.concat(@data.map { |v| v.id.to_s })
|
|
25
|
+
|
|
26
|
+
else
|
|
27
|
+
ret.concat(@data.response.map { |v| v[:id].to_s })
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
else
|
|
31
|
+
ret.concat(parameters.keys.map(&:to_s))
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
ret
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def parameters
|
|
38
|
+
@action_dir.action.params
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def title
|
|
42
|
+
'Output parameters'
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
protected
|
|
46
|
+
def new_child(name)
|
|
47
|
+
if child = super
|
|
48
|
+
child
|
|
49
|
+
|
|
50
|
+
elsif !@data
|
|
51
|
+
nil
|
|
52
|
+
|
|
53
|
+
elsif @list
|
|
54
|
+
id = name.to_s.to_i
|
|
55
|
+
|
|
56
|
+
if @data.is_a?(HaveAPI::Client::ResourceInstanceList)
|
|
57
|
+
param = @data.detect { |v| v.id == id }
|
|
58
|
+
[ResourceInstanceDir, param]
|
|
59
|
+
|
|
60
|
+
else
|
|
61
|
+
param = @data.response.detect { |v| v[:id] == id }
|
|
62
|
+
[ListItem, @action_dir.action, :output, param]
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
elsif @action_dir.action.params.has_key?(name)
|
|
66
|
+
[
|
|
67
|
+
Parameter,
|
|
68
|
+
@action_dir.action,
|
|
69
|
+
name,
|
|
70
|
+
:output,
|
|
71
|
+
@data.is_a?(HaveAPI::Client::ResourceInstance) ? @data : @data.response,
|
|
72
|
+
]
|
|
73
|
+
|
|
74
|
+
else
|
|
75
|
+
nil
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
end
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
module HaveAPI::Fs::Components
|
|
2
|
+
class ActionStatus < File
|
|
3
|
+
def initialize(action_dir, *args)
|
|
4
|
+
super(*args)
|
|
5
|
+
@action_dir = action_dir
|
|
6
|
+
@v = nil
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def read
|
|
10
|
+
value.to_s + "\n"
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def set(v)
|
|
14
|
+
changed
|
|
15
|
+
@v = v
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
protected
|
|
19
|
+
def value
|
|
20
|
+
case @v
|
|
21
|
+
when true
|
|
22
|
+
1
|
|
23
|
+
|
|
24
|
+
when false
|
|
25
|
+
0
|
|
26
|
+
|
|
27
|
+
else
|
|
28
|
+
nil
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
module HaveAPI::Fs::Components
|
|
2
|
+
class CacheStats < File
|
|
3
|
+
def read
|
|
4
|
+
c = context.cache
|
|
5
|
+
|
|
6
|
+
{
|
|
7
|
+
size: c.size,
|
|
8
|
+
hits: c.hits,
|
|
9
|
+
misses: c.misses,
|
|
10
|
+
invalid: c.invalid,
|
|
11
|
+
drops: c.drops,
|
|
12
|
+
hitratio: (c.hits.to_f / (c.hits + c.misses + c.invalid) * 100).round(2),
|
|
13
|
+
sweeps: c.runs,
|
|
14
|
+
last_sweep: (c.last_time && c.last_time.iso8601) || '-',
|
|
15
|
+
next_sweep: c.next_time.iso8601,
|
|
16
|
+
}.map { |k, v| sprintf('%-15s %s', "#{k}:", v) }.join("\n") + "\n"
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
module HaveAPI::Fs::Components
|
|
2
|
+
class ComponentList < File
|
|
3
|
+
def read
|
|
4
|
+
str = component_list.map do |c|
|
|
5
|
+
sprintf('%-50s %s', c.class.name, c.path)
|
|
6
|
+
end.join("\n")
|
|
7
|
+
str += "\n" unless str.empty?
|
|
8
|
+
str
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
protected
|
|
12
|
+
def component_list(component = nil)
|
|
13
|
+
component ||= parent
|
|
14
|
+
ret = []
|
|
15
|
+
|
|
16
|
+
component.send(:children).each do |_, c|
|
|
17
|
+
ret << c
|
|
18
|
+
ret.concat(component_list(c))
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
ret
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|