sfpagent 0.1.0 → 0.1.1

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of sfpagent might be problematic. Click here for more details.

data/README.md CHANGED
@@ -1,16 +1,14 @@
1
1
  SFP Agent for Ruby
2
2
  ==================
3
3
  - Author: Herry (herry13@gmail.com)
4
- - Version: 0.0.1
5
- - License: [BSD License](https://github.com/herry13/sfp-ruby/blob/master/LICENSE)
4
+ - Version: 0.1.1
5
+ - License: [BSD License](https://github.com/herry13/sfpagent/blob/master/LICENSE)
6
6
 
7
- A gem that provides a Ruby interface to an SFP agent.
7
+ A Ruby script and API of an SFP agent. The agent could be accessed through HTTP RESTful API.
8
8
 
9
-
10
- To install
11
- ----------
12
-
13
- $ gem install sfpagent
9
+ With this agent, you could manage a software component such as get the state, install, uninstall, update
10
+ its configuration, etc. Each configuration should be specified in [SFP language](https://github.com/herry13/sfp).
11
+ Every software component could have a set of methods which could be called through HTTP request.
14
12
 
15
13
 
16
14
  Requirements
@@ -20,3 +18,53 @@ Requirements
20
18
  - sfp (>= 0.3.0)
21
19
  - antlr3
22
20
  - json
21
+
22
+
23
+ To install
24
+ ----------
25
+
26
+ $ gem install sfpagent
27
+
28
+
29
+ As daemon
30
+ ---------
31
+ - start the agent daemon
32
+
33
+ $ sfpagent -s
34
+
35
+ In default, the agent will listen at port **1314**.
36
+
37
+ - stop the agent daemon
38
+
39
+ $ sfpagent -t
40
+
41
+
42
+ Cached Directory
43
+ ----------------
44
+ Cached directory keeps all agent's local data such as:
45
+ - log file
46
+ - PID file
47
+ - model file
48
+ - installed modules
49
+
50
+ In default, the agent will use (and created if not exist):
51
+ - directory **~/.sfpagent**, when the daemon is running with non-root user,
52
+ - directory **/var/sfpagent**, when the daemon is running with root user.
53
+
54
+
55
+ HTTP RESTful API
56
+ ----------------
57
+ - GET
58
+ - /state : return the state of the agent
59
+ - /model : return the model of the agent
60
+ - /modules : return the list of modules
61
+ - /log : return the last 100 lines of the log file
62
+
63
+ - POST
64
+ - /execute : execute an action as specified in "action" parameter
65
+
66
+ - PUT
67
+ - /model : set/replace the model with given model as specified in "model" parameter
68
+ - /module : set/replace the module if "module" parameter is specified, or delete the module if the parameter is not exist
69
+
70
+
data/bin/sfpagent CHANGED
@@ -4,7 +4,7 @@ libdir = File.expand_path(File.dirname(__FILE__))
4
4
  require "#{libdir}/../lib/sfpagent"
5
5
 
6
6
  opts = Trollop::options do
7
- version "sfpagent 0.0.1 (c) 2013 Herry"
7
+ version "sfpagent 0.1.1 (c) 2013 Herry"
8
8
  banner <<-EOS
9
9
  SFP Agent that provides a Ruby framework for managing system configurations. The configurations are modelled in SFP language.
10
10
 
@@ -9,6 +9,8 @@ require 'logger'
9
9
 
10
10
  module Sfp
11
11
  module Agent
12
+ NetHelper = Object.new.extend(Nuri::Net::Helper)
13
+
12
14
  if Process.euid == 0
13
15
  CachedDir = '/var/sfpagent'
14
16
  else
@@ -185,8 +187,9 @@ module Sfp
185
187
  # Return the current state of the model.
186
188
  #
187
189
  def self.get_state(as_sfp=true)
188
- return nil if !defined? @@runtime or @@runtime.nil?
190
+ return nil if !defined?(@@runtime) or @@runtime.nil?
189
191
  begin
192
+ @@runtime.get_state if @@runtime.modules.nil?
190
193
  return @@runtime.get_state(as_sfp)
191
194
  rescue Exception => e
192
195
  @@logger.error "Get state [Failed] #{e}\n#{e.backtrace.join("\n")}"
@@ -194,6 +197,39 @@ module Sfp
194
197
  false
195
198
  end
196
199
 
200
+ def self.resolve(path, as_sfp=true)
201
+ return Sfp::Undefined.new if !defined?(@@runtime) or @@runtime.nil? or @@runtime.modules.nil?
202
+ begin
203
+ path = path.simplify
204
+ _, node, _ = path.split('.', 3)
205
+ if @@runtime.modules.has_key?(node)
206
+ # local resolve
207
+ parent, attribute = path.pop_ref
208
+ mod = @@runtime.modules.at?(parent)
209
+ if mod.is_a?(Hash)
210
+ mod[:_self].update_state
211
+ state = mod[:_self].state
212
+ return state[attribute] if state.has_key?(attribute)
213
+ end
214
+ else
215
+ agents = get_agents
216
+ if agents[node].is_a?(Hash)
217
+ agent = agents[node]
218
+ path = path[1, path.length-1].gsub /\./, '/'
219
+ code, data = NetHelper.get_data(agent['sfpAddress'], agent['sfpPort'], "/state#{path}")
220
+ if code.to_i == 200
221
+ state = JSON[data]['state']
222
+ return Sfp::Unknown.new if state == '<sfp::unknown>'
223
+ return state if !state.is_a?(String) or state[0,15] != '<sfp::undefined'
224
+ end
225
+ end
226
+ end
227
+ rescue Exception => e
228
+ @@logger.error "Resolve #{path} [Failed] #{e}\n#{e.backtrace.join("\n")}"
229
+ end
230
+ Sfp::Undefined.new
231
+ end
232
+
197
233
  # Execute an action
198
234
  #
199
235
  # @param action contains the action's schema.
@@ -342,13 +378,13 @@ module Sfp
342
378
  raise Exception, "Invalid agents list." if not agents.is_a?(Hash)
343
379
  buffer = {}
344
380
  agents.each { |name,data|
345
- raise Exception "Invalid agents list." if not data.is_a?(Hash) or
346
- not data.has_key?('address') or data['address'].to_s.strip == '' or
347
- not data.has_key?('port')
381
+ raise Exception, "Invalid agents list." if not data.is_a?(Hash) or
382
+ not data.has_key?('sfpAddress') or data['sfpAddress'].to_s.strip == '' or
383
+ not data.has_key?('sfpPort')
348
384
  buffer[name] = {}
349
- buffer[name]['address'] = data['address'].to_s
350
- buffer[name]['port'] = data['port'].to_s.strip.to_i
351
- buffer[name]['port'] = DefaultPort if buffer[name]['port'] == 0
385
+ buffer[name]['sfpAddress'] = data['sfpAddress'].to_s
386
+ buffer[name]['sfpPort'] = data['sfpPort'].to_s.strip.to_i
387
+ buffer[name]['sfpPort'] = DefaultPort if buffer[name]['sfpPort'] == 0
352
388
  }
353
389
  f.write(JSON.generate(buffer))
354
390
  f.flush
@@ -408,6 +444,9 @@ module Sfp
408
444
  elsif path == '/modules'
409
445
  status, content_type, body = [200, 'application/json', JSON.generate(Sfp::Agent.get_modules)]
410
446
 
447
+ elsif path == '/agents'
448
+ status, content_type, body = [200, 'application/JSON', JSON.generate(Sfp::Agent.get_agents)]
449
+
411
450
  elsif path == '/log'
412
451
  status, content_type, body = [200, 'text/plain', Sfp::Agent.get_log(100)]
413
452
 
@@ -423,6 +462,8 @@ module Sfp
423
462
  #
424
463
  # uri:
425
464
  # /execute => receive an action's schema and execute it
465
+ # /migrate => SFP object migration
466
+ # /duplicate => SFP object duplication
426
467
  #
427
468
  def do_POST(request, response)
428
469
  status = 400
@@ -433,6 +474,14 @@ module Sfp
433
474
  path = (request.path[-1,1] == '/' ? ryyequest.path.chop : request.path)
434
475
  if path == '/execute'
435
476
  status, content_type, body = self.execute({:query => request.query})
477
+
478
+ elsif path =~ /\/migrate\/.+/
479
+ status, content_type, body = self.migrate({:src => path[8, path.length-8],
480
+ :dest => request.query['destination']})
481
+
482
+ elsif path =~ /\/duplicate\/.+/
483
+ # TODO
484
+
436
485
  end
437
486
  end
438
487
 
@@ -458,13 +507,9 @@ module Sfp
458
507
  if path == '/model'
459
508
  status, content_type, body = self.set_model({:query => request.query})
460
509
 
461
- elsif path =~ /\/module\/.+/
462
- status, content_type, body = self.manage_modules({:name => path[8, path.length-8],
463
- :query => request.query})
464
-
465
510
  elsif path =~ /\/modules\/.+/
466
511
  status, content_type, body = self.manage_modules({:name => path[9, path.length-9],
467
- :query => request.query})
512
+ :query => request.query})
468
513
 
469
514
  elsif path == '/modules'
470
515
  status, content_type, body = self.manage_modules({:delete => true})
@@ -480,6 +525,35 @@ module Sfp
480
525
  response.body = body
481
526
  end
482
527
 
528
+ def migrate(p={})
529
+ # migrate: source path, destination path
530
+ #@logger.info "migrate #{p[:src]} => #{p[:dest]}"
531
+ return [400, 'plain/text', 'Destination path should begin with "/"'] if p[:dest].to_s[0,1] != '/'
532
+ begin
533
+ # reformat the source and destination paths to SFP reference
534
+ p[:src] = '$' + p[:src].gsub(/\//, '.')
535
+ p[:dest] = '$' + p[:dest].gsub(/\//, '.')
536
+
537
+ # find the target in agents' database
538
+ agents = Sfp::Agent.get_agents
539
+ data = agents.at?(p[:dest])
540
+ return [404, 'plain/text', 'Unrecognized destination!'] if !data.is_a?(Hash)
541
+
542
+ # send the sub-model to destination
543
+ model = Sfp::Agent.get_model
544
+ return [404, '', ''] if model.nil?
545
+ submodel = model.at?(p[:src])
546
+
547
+ # TODO
548
+ # 1. send the configuration to destination
549
+
550
+ return [200, 'plain/text', "#{p[:src]} #{p[:dest]}:#{data.inspect}"]
551
+ rescue Exception => e
552
+ @logger.error "Migration failed #{e}\n#{e.backtrace.join("\n")}"
553
+ end
554
+ return [500, 'plain/text', e.to_s]
555
+ end
556
+
483
557
  def manage_agents(p={})
484
558
  begin
485
559
  if p[:query].has_key?('agents')
@@ -2,6 +2,7 @@ require 'etc'
2
2
  require 'fileutils'
3
3
 
4
4
  module Sfp::Resource
5
+ attr_accessor :parent
5
6
  attr_reader :state, :model
6
7
 
7
8
  def init(model, default)
@@ -23,6 +24,10 @@ module Sfp::Resource
23
24
 
24
25
  alias_method :reset, :to_model
25
26
 
27
+ def resolve(path)
28
+ Sfp::Agent.resolve(path.simplify)
29
+ end
30
+
26
31
  protected
27
32
  def exec_seq(*commands)
28
33
  commands = [commands.to_s] if not commands.is_a?(Array)
@@ -0,0 +1,56 @@
1
+ require 'uri'
2
+ require 'net/http'
3
+
4
+ module Nuri::Net
5
+ end
6
+
7
+ module Nuri::Net::Helper
8
+ def http_request(uri, request, open_timeout=5, read_timeout=1800)
9
+ http = Net::HTTP.new(uri.host, uri.port)
10
+ http.open_timeout = open_timeout
11
+ http.read_timeout = read_timeout
12
+ http.start
13
+ http.request(request) { |res| return [res.code, res.body] }
14
+ end
15
+
16
+ def post_data(address, port, path, data, open_timeout=5, read_timeout=1800)
17
+ address = address.to_s.strip
18
+ port = port.to_s.strip
19
+ path = path.to_s.strip
20
+ raise Exception, "Invalid parameters [address:#{address},port:#{port},path:#{path}]" if
21
+ address.length <= 0 or port.length <= 0 or path.length <= 0
22
+
23
+ path.sub!(/^\/+/, '')
24
+ url = URI.parse("http://#{address}:#{port}/#{path}")
25
+ req = Net::HTTP::Post.new(url.path)
26
+ req.set_form_data(data)
27
+ http_request(url, req, open_timeout, read_timeout)
28
+ end
29
+
30
+ def put_data(address, port, path, data, open_timeout=5, read_timeout=1800)
31
+ address = address.to_s.strip
32
+ port = port.to_s.strip
33
+ path = path.to_s.strip
34
+ raise Exception, "Invalid parameters [address:#{address},port:#{port},path:#{path}]" if
35
+ address.length <= 0 or port.length <= 0 or path.length <= 0
36
+
37
+ path.sub!(/^\/+/, '')
38
+ url = URI.parse("http://#{address}:#{port}/#{path}")
39
+ req = Net::HTTP::Put.new(url.path)
40
+ req.set_form_data(data)
41
+ http_request(url, req, open_timeout, read_timeout)
42
+ end
43
+
44
+ def get_data(address, port, path, open_timeout=5, read_timeout=1800)
45
+ address = address.to_s.strip
46
+ port = port.to_s.strip
47
+ path = path.to_s.strip
48
+ raise Exception, "Invalid parameters [address:#{address},port:#{port},path:#{path}]" if
49
+ address.length <= 0 or port.length <= 0 or path.length <= 0
50
+
51
+ path.sub!(/^\/+/, '')
52
+ url = URI.parse("http://#{address}:#{port}/#{path}")
53
+ req = Net::HTTP::Get.new(url.path)
54
+ http_request(url, req, open_timeout, read_timeout)
55
+ end
56
+ end
@@ -1,7 +1,10 @@
1
1
  class Sfp::Runtime
2
+ attr_reader :modules
3
+
2
4
  def initialize(parser)
3
5
  @parser = parser
4
6
  @root = @parser.root
7
+ @modules = nil
5
8
  end
6
9
 
7
10
  def execute_action(action)
@@ -11,7 +14,7 @@ class Sfp::Runtime
11
14
  p
12
15
  end
13
16
 
14
- self.get_state if not defined? @modules
17
+ self.get_state if not defined?(@modules) or @modules.nil?
15
18
 
16
19
  module_path, method_name = action['name'].pop_ref
17
20
  mod = @modules.at?(module_path)[:_self]
@@ -23,20 +26,24 @@ class Sfp::Runtime
23
26
  end
24
27
 
25
28
  def get_state(as_sfp=false)
26
- def cleanup(value)
27
- #value.accept(SfpState.new)
28
- #value
29
- value.select { |k,v| k[0,1] != '_' and !(v.is_a?(Hash) and v['_context'] != 'object') }
30
- #value.keys.each { |k| value[k] = cleanup(value[k]) if value[k].is_a?(Hash) }
29
+ def cleanup(model)
30
+ model.select { |k,v| k[0,1] != '_' and !(v.is_a?(Hash) and v['_context'] != 'object') }
31
+ end
32
+
33
+ def add_hidden_attributes(model, state)
34
+ model.each { |k,v|
35
+ state[k] = v if (k[0,1] == '_' and k != '_parent') or
36
+ (v.is_a?(Hash) and v['_context'] == 'procedure')
37
+ }
31
38
  end
32
39
 
33
40
  # Load the implementation of an object, and return its current state
34
- # @param value a Hash
41
+ # @param model a Hash
35
42
  # @return a Hash which is the state of the object
36
43
  #
37
- def get_module_state(value, root, as_sfp=false)
44
+ def instantiate_module(model, root, as_sfp=false)
38
45
  # extract class name
39
- class_name = value['_isa'].sub(/^\$\./, '') # [2, value['_isa'].length]
46
+ class_name = model['_isa'].sub(/^\$\./, '')
40
47
 
41
48
  # throw an exception if schema's implementation is not exist!
42
49
  raise Exception, "Implementation of schema #{class_name} is not available!" if
@@ -44,45 +51,55 @@ class Sfp::Runtime
44
51
 
45
52
  # create an instance of the schema
46
53
  mod = Sfp::Module::const_get(class_name).new
47
- default = cleanup(root.at?(value['_isa']))
48
- model = cleanup(value)
49
- mod.init(model, default)
50
-
51
- # update and get state
52
- mod.update_state
53
- state = mod.state
54
-
55
- # insert all hidden attributes, except "_parent"
56
- value.each do |k,v|
57
- state[k] = v if (k[0,1] == '_' and k != '_parent') or
58
- (v.is_a?(Hash) and v['_context'] == 'procedure')
59
- end if as_sfp
60
- [mod, state]
54
+ default = cleanup(root.at?(model['_isa']))
55
+ ruby_model = cleanup(model)
56
+ mod.init(ruby_model, default)
57
+ mod
61
58
  end
62
59
 
63
60
  # Return the state of an object
64
61
  #
65
- def get_object_state(value, root, as_sfp=false)
62
+ def get_object_state(model, root, as_sfp=false, path='$')
66
63
  modules = {}
67
64
  state = {}
68
- if value['_context'] == 'object' and value['_isa'].to_s.isref
69
- if value['_isa'] != '$.Object'
70
- # if this value is an instance of a subclass of Object, then
65
+ if model['_context'] == 'object' and model['_isa'].to_s.isref
66
+ if model['_isa'] != '$.Object'
67
+ # if this model is an instance of a subclass of Object, then
71
68
  # get the current state of this object
72
- modules[:_self], state = get_module_state(value, root, as_sfp)
69
+ #modules[:_self] = nil
70
+ mod = (!defined?(@modules) or @modules.nil? ? nil : @modules.at?(path))
71
+ if mod.is_a?(Hash)
72
+ modules[:_self] = mod[:_self]
73
+ else
74
+ # the module has not been instantiated yet!
75
+ modules[:_self] = instantiate_module(model, root, as_sfp)
76
+ end
77
+ # update and get the state
78
+ modules[:_self].update_state
79
+ state = modules[:_self].state
80
+ if !mod.nil? and mod.has_key?(:_vars)
81
+ state.keep_if { |k,v| mod[:_vars].index(k) }
82
+ modules[:_vars] = mod[:_vars]
83
+ else
84
+ modules[:_vars] = state.keys
85
+ end
86
+ # set hidden attributes
87
+ add_hidden_attributes(model, state) if as_sfp
73
88
  end
74
89
  end
75
90
 
76
91
  # get the state for each attributes which are not covered by this
77
92
  # object's module
78
- (value.keys - state.keys).each { |key|
93
+ (model.keys - state.keys).each do |key|
79
94
  next if key[0,1] == '_'
80
- if value[key].is_a?(Hash)
81
- modules[key], state[key] = get_object_state(value[key], root, as_sfp) if value[key]['_context'] == 'object'
95
+ if model[key].is_a?(Hash)
96
+ modules[key], state[key] = get_object_state(model[key], root, as_sfp, path.push(key)) if
97
+ model[key]['_context'] == 'object'
98
+ modules[key]['_parent'] = modules if modules[key].is_a?(Hash)
82
99
  else
83
100
  state[key] = Sfp::Undefined.new
84
101
  end
85
- }
102
+ end
86
103
 
87
104
  [modules, state]
88
105
  end
@@ -91,6 +108,8 @@ class Sfp::Runtime
91
108
  root.accept(Sfp::Visitor::ParentEliminator.new)
92
109
  @modules, state = get_object_state(root, root, as_sfp)
93
110
 
111
+ @modules.accept(ParentGenerator)
112
+
94
113
  state
95
114
  end
96
115
 
@@ -112,6 +131,12 @@ class Sfp::Runtime
112
131
  end
113
132
  end
114
133
 
134
+ ParentGenerator = Object.new
135
+ def ParentGenerator.visit(name, value, parent)
136
+ value['_parent'] = parent if value.is_a?(Hash)
137
+ true
138
+ end
139
+
115
140
  def execute_sequential_plan(plan)
116
141
  puts 'Execute a sequential plan...'
117
142
 
@@ -127,120 +152,3 @@ class Sfp::Runtime
127
152
  true
128
153
  end
129
154
  end
130
-
131
- =begin
132
- def execute(plan)
133
- plan = JSON.parse(plan)
134
- if plan['type'] == 'sequential'
135
- execute_sequential(plan)
136
- else
137
- execute_parallel(plan)
138
- end
139
- end
140
-
141
-
142
- def execute_sequential(plan)
143
- puts 'Execute a sequential plan...'
144
-
145
- plan['workflow'].each_index { |index|
146
- action = plan['workflow'][index]
147
- print "#{index+1}) #{action['name']} "
148
-
149
- module_path, method_name = action['name'].pop_ref
150
- mod = @modules.at?(module_path)[:_self]
151
- raise Exception, "Cannot execute #{action['name']}!" if not mod.respond_to?(method_name)
152
- if not mod.send method_name.to_sym, normalise_parameters(action['parameters'])
153
- puts '[Failed]'
154
- return false
155
- end
156
-
157
- puts '[OK]'
158
- }
159
- true
160
- end
161
-
162
- def execute_parallel(plan)
163
- # TODO
164
- puts 'Execute a parallel plan...'
165
- false
166
- end
167
- =end
168
-
169
- =begin
170
- def plan
171
- # generate initial state
172
- task = { 'initial' => Sfp::Helper.to_state('initial', self.get_state(true)) }
173
-
174
- # add schemas
175
- @root.each { |k,v|
176
- next if !v.is_a?(Hash) or v['_context'] != 'class'
177
- task[k] = v
178
- }
179
-
180
- # add goal constraint
181
- model = @root.select { |k,v| v.is_a?(Hash) and v['_context'] == 'object' }
182
- goalgen = Sfp::Helper::GoalGenerator.new
183
- model.accept(goalgen)
184
- task['goal'] = goalgen.results
185
-
186
- # remove old parent links
187
- task.accept(Sfp::Visitor::ParentEliminator.new)
188
-
189
- # reconstruct Sfp parent links
190
- task.accept(Sfp::Visitor::SfpGenerator.new(task))
191
-
192
- # solve and return the plan solution
193
- planner = Sfp::Planner.new
194
- planner.solve({:sfp => task, :pretty_json => true})
195
- end
196
- =end
197
-
198
-
199
-
200
- =begin
201
- module Helper
202
- def self.create_object(name)
203
- { '_self' => name, '_context' => 'object', '_isa' => '$.Object', '_classes' => ['$.Object'] }
204
- end
205
-
206
- def self.create_state(name)
207
- { '_self' => name, '_context' => 'state' }
208
- end
209
-
210
- def self.to_state(name, value)
211
- raise Exception, 'Given value should be a Hash!' if not value.is_a?(Hash)
212
- value['_self'] = name
213
- value['_context'] = 'state'
214
- value
215
- end
216
-
217
- module Constraint
218
- def self.equals(value)
219
- { '_context' => 'constraint', '_type' => 'equals', '_value' => value }
220
- end
221
-
222
- def self.and(name)
223
- { '_context' => 'constraint', '_type' => 'and', '_self' => name }
224
- end
225
- end
226
-
227
- class GoalGenerator
228
- attr_reader :results
229
-
230
- def initialize
231
- @results = Sfp::Helper::Constraint.and('goal')
232
- end
233
-
234
- def visit(name, value, parent)
235
- return false if name[0,1] == '_'
236
- if value.is_a?(Hash)
237
- return true if value['_context'] == 'object'
238
- return false
239
- end
240
-
241
- @results[ parent.ref.push(name) ] = Sfp::Helper::Constraint.equals(value)
242
- false
243
- end
244
- end
245
- end
246
- =end
data/lib/sfpagent.rb CHANGED
@@ -3,12 +3,14 @@ require 'rubygems'
3
3
  require 'json'
4
4
  require 'sfp'
5
5
 
6
+ module Nuri
7
+ end
8
+
6
9
  # internal dependencies
7
10
  libdir = File.expand_path(File.dirname(__FILE__))
8
11
 
12
+ require libdir + '/sfpagent/net_helper.rb'
9
13
  require libdir + '/sfpagent/executor.rb'
10
-
11
14
  require libdir + '/sfpagent/runtime.rb'
12
15
  require libdir + '/sfpagent/module.rb'
13
-
14
16
  require libdir + '/sfpagent/agent.rb'
data/sfpagent.gemspec CHANGED
@@ -1,6 +1,6 @@
1
1
  Gem::Specification.new do |s|
2
2
  s.name = 'sfpagent'
3
- s.version = '0.1.0'
3
+ s.version = '0.1.1'
4
4
  s.date = '2013-07-03'
5
5
  s.summary = 'SFP Agent'
6
6
  s.description = 'A Ruby implementation of SFP agent.'
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sfpagent
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.1.1
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -13,7 +13,7 @@ date: 2013-07-03 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: sfp
16
- requirement: !ruby/object:Gem::Requirement
16
+ requirement: &17002580 !ruby/object:Gem::Requirement
17
17
  none: false
18
18
  requirements:
19
19
  - - ~>
@@ -21,12 +21,7 @@ dependencies:
21
21
  version: 0.3.0
22
22
  type: :runtime
23
23
  prerelease: false
24
- version_requirements: !ruby/object:Gem::Requirement
25
- none: false
26
- requirements:
27
- - - ~>
28
- - !ruby/object:Gem::Version
29
- version: 0.3.0
24
+ version_requirements: *17002580
30
25
  description: A Ruby implementation of SFP agent.
31
26
  email: herry13@gmail.com
32
27
  executables:
@@ -43,6 +38,7 @@ files:
43
38
  - lib/sfpagent/agent.rb
44
39
  - lib/sfpagent/executor.rb
45
40
  - lib/sfpagent/module.rb
41
+ - lib/sfpagent/net_helper.rb
46
42
  - lib/sfpagent/runtime.rb
47
43
  - sfpagent.gemspec
48
44
  homepage: https://github.com/herry13/sfpagent
@@ -66,7 +62,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
66
62
  version: '0'
67
63
  requirements: []
68
64
  rubyforge_project: sfpagent
69
- rubygems_version: 1.8.23
65
+ rubygems_version: 1.8.11
70
66
  signing_key:
71
67
  specification_version: 3
72
68
  summary: SFP Agent