nuri 0.5.3 → 0.5.4
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 +4 -4
- data/README.md +5 -1
- data/VERSION +1 -1
- data/bin/nuri +60 -14
- data/bin/nuri-install-module +17 -9
- data/examples/mockcloud/apache2.sfp +14 -0
- data/examples/mockcloud/ping.rb +38 -0
- data/examples/openstack/openstack-hadoop1-cluster.sfp +37 -0
- data/examples/openstack/openstack-hadoop2-cluster.sfp +39 -0
- data/examples/v2/apache.sfp +30 -0
- data/examples/v2/aptpackage.sfp +6 -0
- data/examples/v2/mock1.sfp +12 -0
- data/examples/v2/package.sfp +22 -0
- data/examples/v2/service.sfp +94 -0
- data/examples/v2/tarpackage.sfp +5 -0
- data/lib/nuri.rb +14 -10
- data/lib/nuri/choreographer.rb +3 -3
- data/lib/nuri/helper.rb +20 -10
- data/lib/nuri/master.rb +82 -54
- data/lib/nuri/orchestrator.rb +1 -1
- data/modules/.gitignore +0 -4
- data/modules/README.md +11 -0
- data/modules/apache/apache.sfp +2 -1
- data/modules/file/file.rb +49 -19
- data/modules/hadoop1/hadoop1.rb +18 -11
- data/modules/hadoop2/hadoop2.rb +11 -11
- data/modules/hadoop2/hadoop2.sfp +7 -6
- data/modules/hadoop2/yarn-site.xml +5 -0
- data/modules/machine/machine.rb +24 -14
- data/modules/openstack/README.md +24 -0
- data/modules/openstack/config.yml +5 -0
- data/modules/openstack/example.sfp +9 -0
- data/modules/openstack/openstack.rb +329 -0
- data/modules/openstack/openstack.sfp +24 -0
- data/modules/os/os.rb +1 -1
- data/modules/package2/apt-repo-list.sh +15 -0
- data/modules/package2/package2.rb +213 -43
- data/modules/package2/package2.sfp +3 -2
- data/modules/pyfile/README.md +4 -0
- data/modules/vm/vm.rb +3 -1
- data/modules/vm/vm.sfp +4 -3
- metadata +20 -3
- data/modules/hpcloud/test.sfp +0 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 9d540fda2ce76121b09511e85b2c986141462e3f
|
4
|
+
data.tar.gz: 36872856f912ad6cbd3998fac2721ac4c7713e8b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d5bf64d0730f0f802a28d0464e79905f0c9a1ed60a7e083d6276b93f3eae99a1016eac02a57ccd7f0ff65b4b9c47f92f3828b4379c8fec942c562af643c0cb61
|
7
|
+
data.tar.gz: c593d7e1a00db7e9e8c2e5c95b79e0573b849dcd3f8a6aea7343f34cf762db3de27d6a45c6cee78bd00e63905e8c3f0bcc0f8a57360965c9a70f7180cd3e5b49
|
data/README.md
CHANGED
@@ -58,10 +58,14 @@ Assume that the model of your system is in file "model.sfp".
|
|
58
58
|
|
59
59
|
press 'Y' and enter to execute the plan (if the plan exists).
|
60
60
|
|
61
|
-
- to execute any generated plan
|
61
|
+
- to execute any generated plan with orchestration
|
62
62
|
|
63
63
|
$ nuri plan -m model.sfp -a
|
64
64
|
|
65
|
+
- to execute any generated plan with choreography
|
66
|
+
|
67
|
+
$ nuri bsig -a
|
68
|
+
|
65
69
|
|
66
70
|
Console mode
|
67
71
|
------------
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.5.
|
1
|
+
0.5.4
|
data/bin/nuri
CHANGED
@@ -6,8 +6,6 @@ require "#{dir}/lib/nuri"
|
|
6
6
|
Version = File.read(File.dirname(__FILE__) + "/../VERSION").strip
|
7
7
|
About = "Nuri #{Version} (c) 2013"
|
8
8
|
|
9
|
-
Nuri.init
|
10
|
-
|
11
9
|
class Nuri::Console
|
12
10
|
include Nuri::Helper
|
13
11
|
|
@@ -27,7 +25,7 @@ class Nuri::Console
|
|
27
25
|
def do_state(args=ARGV, cmd="nuri ")
|
28
26
|
parser = Trollop::Parser.new do
|
29
27
|
banner <<-EOS
|
30
|
-
Usage: #{cmd}state [options]
|
28
|
+
Usage: #{cmd}state [options] [path]
|
31
29
|
where [options] are:
|
32
30
|
EOS
|
33
31
|
opt :model_file, 'file contains a model of desired state', :default => Nuri.main, :short => '-m'
|
@@ -48,12 +46,17 @@ EOS
|
|
48
46
|
state = master.get_state opts
|
49
47
|
state.accept(Sfp::Helper::Sfp2Ruby)
|
50
48
|
|
49
|
+
if args.length > 0
|
50
|
+
state = state.at?("$.#{args[0]}")
|
51
|
+
puts (!opts[:color] ? "Path: #{args[0]}" : "Path: " + "#{args[0]}".yellow)
|
52
|
+
end
|
53
|
+
|
51
54
|
if opts[:json]
|
52
55
|
puts (!opts[:color] ? JSON.generate(state) : CodeRay.encode(JSON.pretty_generate(state), :json, :terminal))
|
53
56
|
else
|
54
57
|
puts (!opts[:color] ? YAML.dump(state) : CodeRay.encode(YAML.dump(state), :yaml, :terminal))
|
55
|
-
|
56
58
|
end
|
59
|
+
|
57
60
|
else
|
58
61
|
$stderr.puts "Model file '#{opts[:model_file]}' is not exist! Use \"-h\" option for more details.".red
|
59
62
|
|
@@ -73,7 +76,7 @@ EOS
|
|
73
76
|
opt :color, 'enable colorized output'
|
74
77
|
opt :no_interactive, 'disable interactive input'
|
75
78
|
opt :no_push_module, 'disable automatic push module'
|
76
|
-
opt :image, 'image graph of the generated plan', :default => Dir.home + '/.nuri/plan.png'
|
79
|
+
opt :image, 'image graph of the generated plan', :default => '' #Dir.home + '/.nuri/plan.png'
|
77
80
|
end
|
78
81
|
help, args = check_help(args)
|
79
82
|
opts = process_args args, parser
|
@@ -92,7 +95,7 @@ EOS
|
|
92
95
|
plan_file = Dir.home + '/.nuri/plan.json'
|
93
96
|
File.open(plan_file, 'w') { |f| f.write(JSON.generate(plan)) }
|
94
97
|
|
95
|
-
if opts[:image]
|
98
|
+
if opts[:image].strip.length > 0
|
96
99
|
system "#{File.dirname(__FILE__)}/nuri-sfwgraph #{plan_file} #{opts[:image]}"
|
97
100
|
end
|
98
101
|
|
@@ -147,25 +150,60 @@ Usage: #{cmd}bsig [options]
|
|
147
150
|
where [options] are:
|
148
151
|
EOS
|
149
152
|
opt :model_file, 'file contains a model of desired state', :default => Nuri.main, :short => '-m'
|
153
|
+
opt :apply, 'generate and then deploy a BSig model'
|
154
|
+
opt :status, 'get BSig status of all agents'
|
150
155
|
opt :purge, 'purge existing BSig model', :short => '-g'
|
151
|
-
opt :deploy, 'generate and then deploy a BSig model'
|
152
156
|
opt :color, 'enable colorized output'
|
153
157
|
opt :no_interactive, 'disable interactive input'
|
158
|
+
opt :no_push_module, 'disable automatic push module'
|
154
159
|
end
|
155
160
|
help, args = check_help(args)
|
156
161
|
opts = process_args args, parser
|
157
162
|
|
158
163
|
if help
|
159
164
|
parser.educate(STDOUT)
|
165
|
+
|
166
|
+
elsif opts[:status]
|
167
|
+
opts[:push_modules] = true if !opts[:no_push_module]
|
168
|
+
master = Nuri::Master.new
|
169
|
+
master.set_model opts
|
170
|
+
master.get_state(opts).each do |name,state|
|
171
|
+
print "- #{name}: "
|
172
|
+
if not state.is_a?(Hash) or not state.has_key?('sfpAddress')
|
173
|
+
puts (opts[:color] ? "unknown address".yellow : "unknown address")
|
174
|
+
else
|
175
|
+
address = state['sfpAddress']
|
176
|
+
port = (state['sfpPort'] ? state['sfpPort'] : 1314)
|
177
|
+
if address.is_a?(String) and port.is_a?(Fixnum)
|
178
|
+
status = Net::HTTP.get(URI("http://#{address}:#{port}/bsig/flaws"))
|
179
|
+
if status == '{}'
|
180
|
+
puts (opts[:color] ? "no-flaws".green : "no-flaws")
|
181
|
+
else
|
182
|
+
puts (opts[:color] ? status.yellow : status)
|
183
|
+
end
|
184
|
+
elsif address.is_a?(Sfp::Undefined)
|
185
|
+
puts (opts[:color] ? "not-exist".yellow : "not-exist")
|
186
|
+
elsif address.is_a?(Sfp::Unknown)
|
187
|
+
puts (opts[:color] ? "unknown".yellow : "unknown")
|
188
|
+
end
|
189
|
+
end
|
190
|
+
end
|
191
|
+
|
160
192
|
elsif File.exist?(opts[:model_file])
|
161
193
|
opts[:bsig_deploy] = true
|
162
194
|
opts[:parallel] = true
|
195
|
+
opts[:push_modules] = true if !opts[:no_push_module]
|
163
196
|
master = Nuri::Master.new
|
164
197
|
master.set_model(opts)
|
165
198
|
|
166
199
|
if opts[:purge]
|
167
200
|
print "Purging Behavioural Signature model "
|
168
|
-
|
201
|
+
puts (opts[:color] ? "[Wait]".yellow : "[Wait]")
|
202
|
+
|
203
|
+
status = master.purge_bsig(opts)
|
204
|
+
print "Purging Behavioural Signature model "
|
205
|
+
|
206
|
+
if status
|
169
207
|
puts (opts[:color] ? "[OK]".green : "[OK]")
|
170
208
|
else
|
171
209
|
puts (opts[:color] ? "[Failed]".red : "[Failed]")
|
@@ -179,17 +217,21 @@ EOS
|
|
179
217
|
json = JSON.pretty_generate(bsig)
|
180
218
|
puts (opts[:color] ? CodeRay.encode(json, :json, :terminal) : json)
|
181
219
|
|
182
|
-
if not opts[:no_interactive] and not opts[:
|
220
|
+
if not opts[:no_interactive] and not opts[:apply]
|
183
221
|
print "Deploy the BSig model [y/N]? "
|
184
|
-
opts[:
|
222
|
+
opts[:apply] = true if STDIN.gets.chomp.upcase == 'Y'
|
185
223
|
end
|
186
224
|
|
187
|
-
if opts[:
|
225
|
+
if opts[:apply]
|
188
226
|
print 'Deploying the BSig model '
|
227
|
+
puts (opts[:color] ? "[Wait]".yellow : "[Wait]")
|
228
|
+
|
189
229
|
opts[:bsig] = bsig
|
190
230
|
if master.deploy_bsig(opts)
|
231
|
+
print 'Deploying the BSig model '
|
191
232
|
puts (opts[:color] ? "[OK]".green : "[OK]")
|
192
233
|
else
|
234
|
+
print 'Deploying the BSig model '
|
193
235
|
puts (opts[:color] ? "[Failed]".red : "[Failed]")
|
194
236
|
end
|
195
237
|
end
|
@@ -400,8 +442,8 @@ EOS
|
|
400
442
|
agents = {}
|
401
443
|
state.each do |name,model|
|
402
444
|
agents[name] = {
|
403
|
-
'sfpAddress' => (model['sfpAddress'].is_a?(String) ? model['sfpAddress'] : ''),
|
404
|
-
'sfpPort' => (model['sfpPort'].is_a?(Fixnum) ? model['sfpPort'] : 0)
|
445
|
+
'sfpAddress' => (model.is_a?(Hash) and model['sfpAddress'].is_a?(String) ? model['sfpAddress'] : ''),
|
446
|
+
'sfpPort' => (model.is_a?(Hash) and model['sfpPort'].is_a?(Fixnum) ? model['sfpPort'] : 0)
|
405
447
|
}
|
406
448
|
end
|
407
449
|
agents
|
@@ -554,4 +596,8 @@ EOS
|
|
554
596
|
end
|
555
597
|
end
|
556
598
|
|
557
|
-
|
599
|
+
if $0 == __FILE__
|
600
|
+
Nuri.init
|
601
|
+
Nuri::Console.new.run
|
602
|
+
end
|
603
|
+
|
data/bin/nuri-install-module
CHANGED
@@ -9,21 +9,29 @@ module Nuri
|
|
9
9
|
module Util
|
10
10
|
TarGzip = Object.new.extend(Util::Tar)
|
11
11
|
|
12
|
-
def self.install_modules(address, port, modules, protocol="http")
|
12
|
+
def self.install_modules(address, port, modules, protocol="http", open_timeout=2, read_timeout=10)
|
13
13
|
data = {}
|
14
14
|
modules.each do |module_name|
|
15
15
|
raise Exception, "Module #{module_name} is not exist!" if not ::File.directory?(module_name)
|
16
16
|
data[module_name] = TarGzip.targzip(module_name, module_name).read
|
17
17
|
end
|
18
18
|
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
19
|
+
begin
|
20
|
+
url = "#{protocol}://#{address}:#{port}/modules"
|
21
|
+
uri = URI.parse(url)
|
22
|
+
http = Net::HTTP.new(uri.host, uri.port)
|
23
|
+
http.open_timeout = open_timeout
|
24
|
+
http.read_timeout = read_timeout
|
25
|
+
request = Net::HTTP::Put.new(uri.request_uri)
|
26
|
+
request.set_form_data(data)
|
27
|
+
response = http.request(request)
|
28
|
+
|
29
|
+
(response.code == '200')
|
30
|
+
rescue Exception => e
|
31
|
+
$stderr.puts "Cannot install module with URL: #{url}"
|
32
|
+
$stderr.puts e
|
33
|
+
false
|
34
|
+
end
|
27
35
|
end
|
28
36
|
end
|
29
37
|
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
include "modules/node/node.sfp"
|
2
|
+
include "modules/apache2/apache2.sfp"
|
3
|
+
include "modules/vm/vm.sfp"
|
4
|
+
include "modules/mockcloud/mockcloud.sfp"
|
5
|
+
|
6
|
+
local isa Node {
|
7
|
+
sfpAddress is "localhost"
|
8
|
+
hpcloud isa MockCloud
|
9
|
+
}
|
10
|
+
vm1 isa VM {
|
11
|
+
apache isa Apache2 {
|
12
|
+
running is true
|
13
|
+
}
|
14
|
+
}
|
@@ -0,0 +1,38 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'net/http'
|
4
|
+
require 'thread'
|
5
|
+
|
6
|
+
len = 120
|
7
|
+
|
8
|
+
def ping(address, port=1314)
|
9
|
+
print "print #{address} "
|
10
|
+
url = "http://#{address}:#{port}/sfpstate"
|
11
|
+
uri = URI.parse(url)
|
12
|
+
http = Net::HTTP.new(uri.host, uri.port)
|
13
|
+
http.open_timeout = 2
|
14
|
+
http.read_timeout = 5
|
15
|
+
begin
|
16
|
+
req = Net::HTTP::Get.new(uri.path)
|
17
|
+
http.start
|
18
|
+
http.request(req) { |res|
|
19
|
+
puts "[#{res.code}] [OK]"
|
20
|
+
}
|
21
|
+
rescue Exception => e
|
22
|
+
puts "[Failed] #{e}" #\n#{e.backtrace.join("\n")}"
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
ping('hadoopmaster')
|
27
|
+
finished = []
|
28
|
+
(1..len).each do |i|
|
29
|
+
# Thread.new {
|
30
|
+
host = "hadoopslave#{i}"
|
31
|
+
ping(host)
|
32
|
+
finished << host
|
33
|
+
# }
|
34
|
+
end
|
35
|
+
|
36
|
+
until finished.length >= len do
|
37
|
+
sleep 1
|
38
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
include "modules/vm/vm.sfp"
|
2
|
+
include "modules/openstack/openstack.sfp"
|
3
|
+
|
4
|
+
proxy isa Node {
|
5
|
+
sfpAddress is "localhost"
|
6
|
+
herry isa OpenStack {
|
7
|
+
// replace the value of OpenStack Identity's URL plus "/tokens/"
|
8
|
+
auth_uri is "http://16.25.166.21:5000/v2.0/tokens/"
|
9
|
+
|
10
|
+
// replace the value with your SSH key name, and then put
|
11
|
+
// the private key (with name <key-name>.pem) in module's directory.
|
12
|
+
vm_ssh_key_name is "nurikey"
|
13
|
+
|
14
|
+
// an image that already has nuri agent
|
15
|
+
vm_image is "8fe14786-5388-4787-8aaf-96feebfa8aae"
|
16
|
+
}
|
17
|
+
}
|
18
|
+
|
19
|
+
include "modules/hadoop1/hadoop1.sfp"
|
20
|
+
|
21
|
+
vm1 isa VM {
|
22
|
+
hadoop isa Hadoop1Master {
|
23
|
+
// replace this with the URL of hadoop1 tar package file
|
24
|
+
source is "http://master.nuri.ext9.sup.hpl.hp.com/hadoop"
|
25
|
+
}
|
26
|
+
}
|
27
|
+
vm2 isa VM {
|
28
|
+
hadoop isa Hadoop1Slave {
|
29
|
+
master is vm1.hadoop
|
30
|
+
// replace this with the URL of hadoop1 tar package file
|
31
|
+
source is "http://master.nuri.ext9.sup.hpl.hp.com/hadoop"
|
32
|
+
}
|
33
|
+
}
|
34
|
+
vm3 extends vm2
|
35
|
+
vm4 extends vm2
|
36
|
+
vm5 extends vm2
|
37
|
+
vm6 extends vm2
|
@@ -0,0 +1,39 @@
|
|
1
|
+
include "modules/vm/vm.sfp"
|
2
|
+
include "modules/openstack/openstack.sfp"
|
3
|
+
|
4
|
+
proxy isa Node {
|
5
|
+
sfpAddress is "localhost"
|
6
|
+
herry isa OpenStack {
|
7
|
+
auth_uri is "http://16.25.166.21:5000/v2.0/tokens/"
|
8
|
+
|
9
|
+
// use m1.medium (id="2") flavor because Hadoop2 requires minimum 4GB of memory
|
10
|
+
vm_flavor is "3"
|
11
|
+
|
12
|
+
// - replace the value with your key name
|
13
|
+
// - put the key file <key-name>.pem in module's directory
|
14
|
+
vm_ssh_key_name is "nurikey"
|
15
|
+
|
16
|
+
// a VM image that already has Nuri agent
|
17
|
+
vm_image is "8fe14786-5388-4787-8aaf-96feebfa8aae"
|
18
|
+
}
|
19
|
+
}
|
20
|
+
|
21
|
+
include "modules/hadoop2/hadoop2.sfp"
|
22
|
+
|
23
|
+
vm1 isa VM {
|
24
|
+
hadoop isa Hadoop2Master {
|
25
|
+
// replace this with the URL of hadoop2 tar package file
|
26
|
+
source is "http://master.nuri.ext9.sup.hpl.hp.com/hadoop"
|
27
|
+
}
|
28
|
+
}
|
29
|
+
vm2 isa VM {
|
30
|
+
hadoop isa Hadoop2Slave {
|
31
|
+
master is vm1.hadoop
|
32
|
+
// replace this with the URL of hadoop2 tar package file
|
33
|
+
source is "http://master.nuri.ext9.sup.hpl.hp.com/hadoop"
|
34
|
+
}
|
35
|
+
}
|
36
|
+
vm3 extends vm2
|
37
|
+
vm4 extends vm2
|
38
|
+
vm5 extends vm2
|
39
|
+
vm6 extends vm2
|
@@ -0,0 +1,30 @@
|
|
1
|
+
include "service.sfp"
|
2
|
+
|
3
|
+
schema Apache extends Service {
|
4
|
+
package_name = "apache2"
|
5
|
+
service_name = "apache2"
|
6
|
+
installed = true
|
7
|
+
running = true
|
8
|
+
configured = true
|
9
|
+
|
10
|
+
final port = 80
|
11
|
+
final document_root = "/var/www"
|
12
|
+
final modules isset String
|
13
|
+
final server_name = ""
|
14
|
+
|
15
|
+
// load balancer config
|
16
|
+
final load_balancer = false
|
17
|
+
final lb_members isset Node
|
18
|
+
final lb_method = "byrequests" // byrequests, bytraffic, bybusyness
|
19
|
+
|
20
|
+
sub configure {
|
21
|
+
condition {
|
22
|
+
this.installed = true
|
23
|
+
this.running = false
|
24
|
+
this.configured = false
|
25
|
+
}
|
26
|
+
effect {
|
27
|
+
this.configured = true
|
28
|
+
}
|
29
|
+
}
|
30
|
+
}
|
@@ -0,0 +1,22 @@
|
|
1
|
+
schema Package {
|
2
|
+
installed = true
|
3
|
+
|
4
|
+
final package_name = ""
|
5
|
+
final version = ""
|
6
|
+
final source = ""
|
7
|
+
|
8
|
+
synchronized sub install {
|
9
|
+
effect {
|
10
|
+
this.installed = true
|
11
|
+
}
|
12
|
+
}
|
13
|
+
|
14
|
+
synchronized sub uninstall {
|
15
|
+
condition {
|
16
|
+
this.installed = true
|
17
|
+
}
|
18
|
+
effect {
|
19
|
+
this.installed = false
|
20
|
+
}
|
21
|
+
}
|
22
|
+
}
|
@@ -0,0 +1,94 @@
|
|
1
|
+
include "aptpackage.sfp"
|
2
|
+
|
3
|
+
schema Service {
|
4
|
+
running = true
|
5
|
+
configured = true
|
6
|
+
|
7
|
+
final service_name = ""
|
8
|
+
|
9
|
+
sub start {
|
10
|
+
condition {
|
11
|
+
this.running != true
|
12
|
+
this.configured = true
|
13
|
+
}
|
14
|
+
effect {
|
15
|
+
this.running = true
|
16
|
+
}
|
17
|
+
}
|
18
|
+
|
19
|
+
sub stop {
|
20
|
+
condition {
|
21
|
+
this.running = true
|
22
|
+
}
|
23
|
+
effect {
|
24
|
+
this.running = false
|
25
|
+
}
|
26
|
+
}
|
27
|
+
|
28
|
+
sub configure {
|
29
|
+
condition {
|
30
|
+
this.running = false
|
31
|
+
}
|
32
|
+
effect {
|
33
|
+
this.configured = true
|
34
|
+
}
|
35
|
+
}
|
36
|
+
}
|
37
|
+
|
38
|
+
schema SysVService extends Service {
|
39
|
+
final runlevel isset String
|
40
|
+
}
|
41
|
+
|
42
|
+
schema AptService extends AptPackage {
|
43
|
+
synchronized sub install {
|
44
|
+
condition {
|
45
|
+
this.installed != true
|
46
|
+
}
|
47
|
+
effect {
|
48
|
+
this.installed = true
|
49
|
+
this.running = false
|
50
|
+
this.configured = false
|
51
|
+
}
|
52
|
+
}
|
53
|
+
|
54
|
+
synchronized sub uninstall {
|
55
|
+
condition {
|
56
|
+
this.running = false
|
57
|
+
this.installed = true
|
58
|
+
}
|
59
|
+
effect {
|
60
|
+
this.installed = false
|
61
|
+
this.configured = true
|
62
|
+
}
|
63
|
+
}
|
64
|
+
|
65
|
+
sub start {
|
66
|
+
condition {
|
67
|
+
this.running != true
|
68
|
+
this.installed = true
|
69
|
+
this.configured = true
|
70
|
+
}
|
71
|
+
effect {
|
72
|
+
this.running = true
|
73
|
+
}
|
74
|
+
}
|
75
|
+
|
76
|
+
sub stop {
|
77
|
+
condition {
|
78
|
+
this.running = true
|
79
|
+
}
|
80
|
+
effect {
|
81
|
+
this.running = false
|
82
|
+
}
|
83
|
+
}
|
84
|
+
|
85
|
+
sub configure {
|
86
|
+
condition {
|
87
|
+
this.installed = true
|
88
|
+
this.running = false
|
89
|
+
}
|
90
|
+
effect {
|
91
|
+
this.configured = true
|
92
|
+
}
|
93
|
+
}
|
94
|
+
}
|