nuri 0.5.3 → 0.5.4
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
}
|