apiotics 0.1.22

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.
Files changed (70) hide show
  1. checksums.yaml +7 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.md +260 -0
  4. data/Rakefile +33 -0
  5. data/lib/apiotics.rb +57 -0
  6. data/lib/apiotics/client.rb +20 -0
  7. data/lib/apiotics/configuration.rb +22 -0
  8. data/lib/apiotics/extract.rb +87 -0
  9. data/lib/apiotics/insert.rb +105 -0
  10. data/lib/apiotics/parse.rb +20 -0
  11. data/lib/apiotics/portal.rb +156 -0
  12. data/lib/apiotics/railtie.rb +14 -0
  13. data/lib/apiotics/server.rb +236 -0
  14. data/lib/apiotics/version.rb +3 -0
  15. data/lib/generators/apiotics/channel/USAGE +10 -0
  16. data/lib/generators/apiotics/channel/channel_generator.rb +35 -0
  17. data/lib/generators/apiotics/channel/templates/apiotics_channel.rb.erb +9 -0
  18. data/lib/generators/apiotics/channel/templates/apiotics_channel_client.coffee.erb +16 -0
  19. data/lib/generators/apiotics/channel/templates/apiotics_channel_initializer.rb.erb +3 -0
  20. data/lib/generators/apiotics/controller/USAGE +8 -0
  21. data/lib/generators/apiotics/controller/controller_generator.rb +24 -0
  22. data/lib/generators/apiotics/controller/templates/apiotics_scaffold.rb.erb +60 -0
  23. data/lib/generators/apiotics/create_model/USAGE +10 -0
  24. data/lib/generators/apiotics/create_model/create_model_generator.rb +76 -0
  25. data/lib/generators/apiotics/create_model/templates/apiotics_logs_model.rb.erb +7 -0
  26. data/lib/generators/apiotics/create_model/templates/apiotics_model.rb.erb +49 -0
  27. data/lib/generators/apiotics/create_model/templates/apiotics_module.rb.erb +10 -0
  28. data/lib/generators/apiotics/create_model/templates/apiotics_module_model.rb.erb +8 -0
  29. data/lib/generators/apiotics/create_model/templates/create_module_model_table.rb.erb +8 -0
  30. data/lib/generators/apiotics/create_table/USAGE +9 -0
  31. data/lib/generators/apiotics/create_table/create_table_generator.rb +52 -0
  32. data/lib/generators/apiotics/create_table/templates/create_logs_table.rb.erb +14 -0
  33. data/lib/generators/apiotics/create_table/templates/create_table.rb.erb +16 -0
  34. data/lib/generators/apiotics/initializer/USAGE +8 -0
  35. data/lib/generators/apiotics/initializer/initializer_generator.rb +27 -0
  36. data/lib/generators/apiotics/initializer/templates/apiotics.rb.erb +10 -0
  37. data/lib/generators/apiotics/initializer/templates/apiotics_module.rb.erb +6 -0
  38. data/lib/generators/apiotics/initializer/templates/apiotics_settings.rb.erb +9 -0
  39. data/lib/generators/apiotics/initializer/templates/apiotics_targets.rb.erb +3 -0
  40. data/lib/generators/apiotics/initializer/templates/setting.rb.erb +3 -0
  41. data/lib/generators/apiotics/install/USAGE +11 -0
  42. data/lib/generators/apiotics/install/install_generator.rb +11 -0
  43. data/lib/generators/apiotics/migration/USAGE +10 -0
  44. data/lib/generators/apiotics/migration/migration_generator.rb +54 -0
  45. data/lib/generators/apiotics/migration/templates/create_logs_table.rb.erb +14 -0
  46. data/lib/generators/apiotics/migration/templates/migrate_table.rb.erb +12 -0
  47. data/lib/generators/apiotics/model/USAGE +11 -0
  48. data/lib/generators/apiotics/model/model_generator.rb +58 -0
  49. data/lib/generators/apiotics/script/USAGE +8 -0
  50. data/lib/generators/apiotics/script/script_generator.rb +44 -0
  51. data/lib/generators/apiotics/script/templates/comm_server.rake +19 -0
  52. data/lib/generators/apiotics/script/templates/dev_comm_server.rake +19 -0
  53. data/lib/generators/apiotics/script/templates/dev_server.rb +8 -0
  54. data/lib/generators/apiotics/script/templates/dev_server_control.rb +7 -0
  55. data/lib/generators/apiotics/script/templates/publish_script.rake +6 -0
  56. data/lib/generators/apiotics/script/templates/script.rb.erb +12 -0
  57. data/lib/generators/apiotics/script/templates/server.rb +8 -0
  58. data/lib/generators/apiotics/script/templates/server_control.rb +7 -0
  59. data/lib/generators/apiotics/script/templates/test_comm_server.rake +19 -0
  60. data/lib/generators/apiotics/script/templates/test_server.rb +8 -0
  61. data/lib/generators/apiotics/script/templates/test_server_control.rb +7 -0
  62. data/lib/generators/apiotics/view/USAGE +12 -0
  63. data/lib/generators/apiotics/view/templates/default.css.erb +18 -0
  64. data/lib/generators/apiotics/view/templates/edit.html.erb +6 -0
  65. data/lib/generators/apiotics/view/templates/form.html.erb +36 -0
  66. data/lib/generators/apiotics/view/templates/index.html.erb +57 -0
  67. data/lib/generators/apiotics/view/templates/show.html.erb +47 -0
  68. data/lib/generators/apiotics/view/view_generator.rb +26 -0
  69. data/lib/tasks/simbiotes_tasks.rake +4 -0
  70. metadata +237 -0
@@ -0,0 +1,105 @@
1
+ # This class takes ruby hashes and saves them to the appropriate database table.
2
+ # All model methods that have corresponding IoT interfaces have six database fields associated with them.
3
+ # The first is a field named the same as the method. The second is name_ack, and third is name_complete, the fourth is name_timestamp, the fifth is name_status, the sixth is name_action.
4
+ # In the hash, instance is the record id (not necessarily the id column), driver is the model, interface is the method.
5
+ # Data should be saved both to the model's database table and to the corresponding log table (name_method_logs).
6
+
7
+ module Apiotics
8
+ class Insert
9
+ def initialize(hash = {"action" => "set-complete", "instance" => 1, "driver" => "test_driver", "interface" => "control", "value" => 72, "timestamp" => "1000000000000", "status" => "ok"})
10
+ @action = hash["action"]
11
+ @id = hash["instance"]
12
+ @class = hash["driver"]
13
+ @methods = hash["interface"]
14
+ @value = hash["value"]
15
+ @timestamp = hash["timestamp"]
16
+ @status = hash["status"]
17
+ find_record
18
+ @methods.each do |k,v|
19
+ @record.send(("#{k}_action=").to_sym, @action)
20
+ @type = @record.class.columns_hash[k].type
21
+ #change_value
22
+ @record.send("#{k}=".to_sym, v)
23
+ @record.send("#{k}_timestamp=".to_sym, @timestamp)
24
+ @record.send("#{k}_status=".to_sym, @status)
25
+ end
26
+ if @action == "set-complete"
27
+ set_complete
28
+ elsif @action == "set-request-ack"
29
+ set_ack
30
+ elsif @action == "get-ack"
31
+ get_ack
32
+ end
33
+ end
34
+
35
+ def find_record
36
+ begin
37
+ klass = @class.classify.constantize
38
+ master_klass = (klass.parent.to_s + "::" + klass.parent.to_s).classify.constantize
39
+ master_record = master_klass.find_by(apiotics_instance: @id)
40
+ @record = master_record.send(@class.demodulize.underscore.downcase)
41
+ #puts e
42
+ end
43
+ end
44
+
45
+ def set_ack
46
+ @methods.each do |k,v|
47
+ @record.send("#{k}_ack=".to_sym, true)
48
+ @record.send("#{k}_complete=".to_sym, false)
49
+ end
50
+ end
51
+
52
+ def set_complete
53
+ @methods.each do |k,v|
54
+ @record.send("#{k}_ack=".to_sym, true)
55
+ @record.send("#{k}_complete=".to_sym, true)
56
+ end
57
+ end
58
+
59
+ def get_ack
60
+ @methods.each do |k,v|
61
+ @record.send("#{k}_ack=".to_sym, true)
62
+ @record.send("#{k}_complete=".to_sym, false)
63
+ end
64
+ end
65
+
66
+ def save
67
+ @record.skip_extract = true
68
+ @record.save
69
+ @record.skip_extract = false
70
+ end
71
+
72
+ def action
73
+ @action.to_s
74
+ end
75
+
76
+ def save_log
77
+ @methods.each do |k,v|
78
+ klass = "#{@class}_#{k}_log".classify.constantize
79
+ r = klass.new
80
+ r.send(("#{k}_action=").to_sym, @action)
81
+ r.send("#{k}=".to_sym, v)
82
+ r.send("#{k}_timestamp=".to_sym, @timestamp)
83
+ r.send("#{k}_status=".to_sym, @status)
84
+ r.send("#{k}_ack=".to_sym, @record.send("#{k}_ack"))
85
+ r.send("#{k}_complete=".to_sym, @record.send("#{k}_complete"))
86
+ r.send("#{@class.classify.demodulize.underscore.gsub("/","_")}_id=".to_sym, @record.id)
87
+ r.save
88
+ end
89
+ end
90
+
91
+ def change_value
92
+ if @type == :boolean
93
+ if @value == "true"
94
+ @value = true
95
+ elsif @value == "false"
96
+ @value = false
97
+ end
98
+ elsif @type == :integer
99
+ @value = @value.to_i
100
+ elsif @type == :string
101
+ end
102
+ end
103
+
104
+ end
105
+ end
@@ -0,0 +1,20 @@
1
+ # This class is responsible for parsing JSON messages and configuration files into ruby hashes.
2
+
3
+ require 'json'
4
+
5
+ module Apiotics
6
+ class Parse
7
+
8
+ def self.message(string)
9
+ JSON.parse(string)
10
+ end
11
+
12
+ def self.config(string)
13
+ JSON.parse(string)
14
+ end
15
+
16
+ def self.portal(string)
17
+ JSON.parse(string)
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,156 @@
1
+
2
+ require 'httparty'
3
+ require 'rest-client'
4
+
5
+ module Apiotics
6
+ class Portal
7
+
8
+ def self.get_attributes(parent_name, model_name)
9
+ configuration = Portal.retrieve_configuration
10
+ attributes = Portal.parse_drivers_and_scripts(configuration, parent_name, model_name)
11
+ end
12
+
13
+ def self.parse_drivers_and_scripts(hash, parent_name, model_name)
14
+ attributes_hash = nil
15
+ hash["workers"][parent_name]["drivers"].each do |k,v|
16
+ name = v["metadata"]["common name"].classify
17
+ if name == model_name.classify
18
+ attributes_hash = Hash.new
19
+ attributes_hash[:kind] = "driver"
20
+ attributes_hash[:attributes] = Hash.new
21
+ v["interfaces"].each do |k,v|
22
+ attributes_hash[:attributes][k] = {
23
+ type: v["type"],
24
+ accessor: v["accessor type"],
25
+ units: v["units"],
26
+ noise: v["noise threshold"],
27
+ values: v["values"],
28
+ range: v["range"]
29
+ }
30
+ end
31
+ end
32
+ end
33
+ hash["workers"][parent_name]["scripts"].each do |k,v|
34
+ name = v["metadata"]["common name"].classify
35
+ if name == model_name.classify
36
+ attributes_hash = Hash.new
37
+ attributes_hash[:kind] = "script"
38
+ attributes_hash[:attributes] = Hash.new
39
+ v["interfaces"].each do |key,value|
40
+ attributes_hash[:attributes][key] = {
41
+ type: value["type"],
42
+ accessor: value["accessor type"],
43
+ units: value["units"],
44
+ noise: value["noise threshold"],
45
+ values: value["values"],
46
+ range: value["range"]
47
+ }
48
+ end
49
+ attributes_hash[:inputs] = Hash.new
50
+ v["inputs"].each do |key,value|
51
+ attributes_hash[:inputs][key] = value
52
+ end
53
+ end
54
+ end
55
+ return attributes_hash
56
+ end
57
+
58
+ def self.retrieve_configuration
59
+ json = HTTParty.post("#{Apiotics.configuration.portal}api/hive", :query => {:public_key => Apiotics.configuration.public_key, :private_key => Apiotics.configuration.private_key}).body
60
+ hash = Apiotics::Parse.portal(json)
61
+ end
62
+
63
+ def self.parse_all_interfaces
64
+ configuration = Portal.retrieve_configuration
65
+ hash = Hash.new
66
+ configuration["workers"].each do |key, value|
67
+ hash[key] = Hash.new
68
+ configuration["workers"][key]["drivers"].each do |k,v|
69
+ a = Array.new
70
+ if v["interfaces"] != nil
71
+ v["interfaces"].each do |key, value|
72
+ a << key
73
+ end
74
+ end
75
+ hash[key][v["metadata"]["common name"]] = a
76
+ end
77
+ configuration["workers"][key]["scripts"].each do |k,v|
78
+ a = Array.new
79
+ if v["interfaces"] != nil
80
+ v["interfaces"].each do |key, value|
81
+ a << key
82
+ end
83
+ end
84
+ hash[key][v["metadata"]["common name"]] = a
85
+ end
86
+ end
87
+ hash
88
+ end
89
+
90
+ def self.sync_device_instances(worker_name)
91
+ instance_hash = JSON.parse(HTTParty.post("#{Apiotics.configuration.portal}api/workers", :query => {:public_key => Apiotics.configuration.public_key, :private_key => Apiotics.configuration.private_key}).body)
92
+ klass = (worker_name.to_s + "::" + worker_name.to_s).constantize rescue nil
93
+ unless klass == nil
94
+ instance_ids = Array.new
95
+ instance_hash[worker_name].each do |instance_id,status|
96
+ instance_ids << instance_id
97
+ end
98
+ stale_instances = klass.where.not(apiotics_instance: instance_ids)
99
+ stale_instances.destroy_all
100
+ instance_ids.each do |instance_id|
101
+ i = klass.find_by(apiotics_instance: instance_id)
102
+ if i == nil
103
+ i = klass.new
104
+ i.apiotics_instance = instance_id
105
+ i.save(:validate => false)
106
+ Apiotics.configuration.targets[worker_name].keys.each do |key|
107
+ subklass = (worker_name + "::" + key).constantize rescue nil
108
+ unless subklass == nil
109
+ s = subklass.new
110
+ k = worker_name.underscore.gsub(" ","_").to_s + "_id="
111
+ s.send((k).to_sym, i.id)
112
+ s.skip_extract = true
113
+ s.save(:validate => false)
114
+ s.skip_extract = false
115
+ end
116
+ end
117
+ end
118
+ end
119
+ end
120
+ end
121
+
122
+ def self.upload_script(worker, script)
123
+ payload = {
124
+ :multipart => true,
125
+ :script_file => File.open("#{Rails.root}/lib/scripts/apiotics/#{worker.underscore.downcase}/#{script.underscore.downcase}.rb", 'rb'),
126
+ :public_key => Apiotics.configuration.public_key,
127
+ :private_key => Apiotics.configuration.private_key,
128
+ :worker_name => worker,
129
+ :script_name => script
130
+ }
131
+ r = RestClient.post("#{Apiotics.configuration.portal}api/upload_script", payload)
132
+ end
133
+
134
+ def self.generate_certificate(csr)
135
+ csr_file = Tempfile.new
136
+ csr_file.write(csr)
137
+ csr_file.rewind
138
+ payload = {
139
+ :multipart => true,
140
+ :csr => csr_file,
141
+ :public_key => Apiotics.configuration.public_key,
142
+ :private_key => Apiotics.configuration.private_key
143
+ }
144
+ r = RestClient.post("#{Apiotics.configuration.portal}api/generate_certificate", payload)
145
+ instance_hash = JSON.parse(r.body)
146
+ return instance_hash["certificate"]
147
+ end
148
+
149
+ def self.ca_certificate
150
+ response = HTTParty.post("#{Apiotics.configuration.portal}api/ca_certificate", :query => {:public_key => Apiotics.configuration.public_key, :private_key => Apiotics.configuration.private_key}).body
151
+ hash = JSON.parse(response)
152
+ return hash["ca_certificate"]
153
+ end
154
+
155
+ end
156
+ end
@@ -0,0 +1,14 @@
1
+ require 'apiotics'
2
+ require 'rails'
3
+
4
+ module Apiotics
5
+ class Railtie < Rails::Railtie
6
+ generators do
7
+ require 'lib/generators/apiotics/model'
8
+ require 'lib/generators/apiotics/create_table'
9
+ require 'lib/generators/apiotics/create_model'
10
+ require 'lib/generators/apiotics/initializer'
11
+ require 'lib/generators/apiotics/migration'
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,236 @@
1
+
2
+ # This class creates a local server listening on port 8001, and connects to a remote server on port 8000. Messages sent to the local server are transmitted
3
+ # to the remote server. Messages received from the remote server are forwarded to the appropriate logic.
4
+
5
+ require "socket"
6
+ require 'openssl'
7
+ require 'json'
8
+
9
+ module Apiotics
10
+ class Server
11
+ def initialize
12
+ @error_msg = nil
13
+ server_details = Server.lookup
14
+ rgs = ApioticsSetting.find_by(key: "server")
15
+ rgs_port = ApioticsSetting.find_by(key: "port")
16
+ if rgs == nil
17
+ if server_details["status"] == "ok"
18
+ rgs = server_details["ip"]
19
+ c = ApioticsSetting.new
20
+ c.key = "server"
21
+ c.value = rgs
22
+ c.save
23
+ rgs_port = server_details["port"]
24
+ c = ApioticsSetting.new
25
+ c.key = "port"
26
+ c.value = rgs_port
27
+ c.save
28
+ else
29
+ @error_msg = server_details["status_msg"]
30
+ end
31
+ else
32
+ if server_details["status"] == "ok"
33
+ rgs.value = server_details["ip"]
34
+ rgs.save
35
+ rgs = rgs.value
36
+ rgs_port.value = server_details["port"]
37
+ rgs_port.save
38
+ rgs_port = rgs_port.value
39
+ else
40
+ @error_msg = server_details["status_msg"]
41
+ end
42
+ end
43
+ if Apiotics.configuration.tls == true
44
+ socket = TCPSocket.new(rgs, rgs_port)
45
+ context = OpenSSL::SSL::SSLContext.new
46
+ context.key = Server.key
47
+ context.cert = Server.cert
48
+ if Apiotics.configuration.verify_peer == true
49
+ ca_tempfile = Tempfile.new
50
+ ca_tempfile.write Server.ca_cert.to_pem
51
+ ca_tempfile.rewind
52
+ context.ca_file = ca_tempfile.path
53
+ context.verify_mode = OpenSSL::SSL::VERIFY_PEER
54
+ end
55
+ server = OpenSSL::SSL::SSLSocket.new socket, context
56
+ server.sync_close = true
57
+ server.connect
58
+ if Apiotics.configuration.verify_peer == true
59
+ ca_tempfile.close(true)
60
+ end
61
+ else
62
+ server = TCPSocket.open(rgs, rgs_port)
63
+ end
64
+ @server = server
65
+ @localport = Apiotics.configuration.local_port
66
+ listen_remote
67
+ listen_local
68
+ end
69
+
70
+ def send(msg)
71
+ puts "Message sent: #{msg}"
72
+ @server.puts( msg )
73
+ end
74
+
75
+ def close
76
+ @server.close
77
+ end
78
+
79
+ def listen_remote
80
+ begin
81
+ Thread.new do
82
+ loop do
83
+ msg = @server.gets
84
+ puts msg
85
+ msg_hash = Apiotics::Parse.message(msg)
86
+ r = Apiotics::Insert.new(msg_hash)
87
+ puts "Message received": msg_hash
88
+ if r.action == "set-request-ack" || r.action == "set-complete" || r.action == "get-ack"
89
+ r.save
90
+ unless Apiotics.configuration.local_logging == false
91
+ r.save_log
92
+ end
93
+ end
94
+ end
95
+ end
96
+ rescue => e
97
+ puts e
98
+ listen_remote
99
+ end
100
+
101
+ end
102
+
103
+ def listen_local
104
+ begin
105
+ server = TCPServer.open(@localport)
106
+ if Apiotics.configuration.handshake == true
107
+ self.send('{"action":"connect"}')
108
+ end
109
+ loop do
110
+ Thread.fork(server.accept) do |client|
111
+ s = client.gets
112
+ if @error_msg != nil
113
+ string = '{"error":"' + error_msg + '"}'
114
+ client.puts(string)
115
+ end
116
+ #puts s
117
+ self.send(s)
118
+ end
119
+ end
120
+ rescue => e
121
+ puts e
122
+ listen_local
123
+ end
124
+ end
125
+
126
+ def self.lookup
127
+ if Apiotics.configuration.tls == true
128
+ socket = TCPSocket.new(Apiotics.configuration.server, Apiotics.configuration.server_port)
129
+ context = OpenSSL::SSL::SSLContext.new
130
+ context.key = Server.key
131
+ context.cert = Server.cert
132
+ if Apiotics.configuration.verify_peer == true
133
+ ca_tempfile = Tempfile.new
134
+ ca_tempfile.write Server.ca_cert.to_pem
135
+ ca_tempfile.rewind
136
+ context.ca_file = ca_tempfile.path
137
+ context.verify_mode = OpenSSL::SSL::VERIFY_PEER
138
+ end
139
+ server = OpenSSL::SSL::SSLSocket.new socket, context
140
+ server.sync_close = true
141
+ server.connect
142
+ else
143
+ server = TCPSocket.open(Apiotics.configuration.server, Apiotics.configuration.server_port)
144
+ end
145
+ server.puts('{"action":"lookup"}')
146
+ msg = server.gets
147
+ hash = JSON.parse(msg)
148
+ server.close
149
+ if Apiotics.configuration.verify_peer == true
150
+ ca_tempfile.close(true)
151
+ end
152
+ return hash
153
+ end
154
+
155
+ def self.key
156
+ key = ApioticsSetting.find_by(key: "key")
157
+ if key == nil
158
+ key = Server.generate_key
159
+ else
160
+ key = key.value
161
+ end
162
+ pass_phrase = ApioticsSetting.find_by(key: "key_pass_phrase").value
163
+ key = OpenSSL::PKey::RSA.new key, pass_phrase
164
+ return key
165
+ end
166
+
167
+ def self.cert
168
+ cert = ApioticsSetting.find_by(key: "cert")
169
+ if cert == nil
170
+ key = Server.key
171
+ public_key = key.public_key
172
+ cert = Server.generate_cert(key, public_key)
173
+ c = ApioticsSetting.new
174
+ c.key = "cert"
175
+ c.value = cert
176
+ c.save
177
+ else
178
+ cert = cert.value
179
+ end
180
+ cert = OpenSSL::X509::Certificate.new cert
181
+ return cert
182
+ end
183
+
184
+ def self.generate_key
185
+ key = OpenSSL::PKey::RSA.new 2048
186
+ pass_phrase = 'simbiotes'
187
+ cipher = OpenSSL::Cipher.new 'AES-128-CBC'
188
+ s = ApioticsSetting.new
189
+ s.key = "key_cipher"
190
+ s.value = "OpenSSL::Cipher.new 'AES-128-CBC'"
191
+ s.save
192
+ s = ApioticsSetting.new
193
+ s.key = "key_pass_phrase"
194
+ s.value = 'simbiotes'
195
+ s.save
196
+ s = ApioticsSetting.new
197
+ s.key = "public_key"
198
+ s.value = key.public_key.to_pem
199
+ s.save
200
+ s = ApioticsSetting.new
201
+ s.key = "key"
202
+ s.value = key.export(cipher, pass_phrase)
203
+ s.save
204
+ return s.value
205
+ end
206
+
207
+ def self.generate_cert(key, public_key)
208
+ csr = OpenSSL::X509::Request.new
209
+ csr.version = 0
210
+ csr.subject = OpenSSL::X509::Name.parse "CN=simbiotes.com/O=#{Apiotics.configuration.public_key}/OU=#{Apiotics.configuration.private_key}"
211
+ csr.public_key = key.public_key
212
+ csr.sign key, OpenSSL::Digest::SHA1.new
213
+ cert = Apiotics::Portal.generate_certificate(csr)
214
+ return cert
215
+ end
216
+
217
+ def self.ca_cert
218
+ ca_cert = ApioticsSetting.find_by(key: "ca_cert")
219
+ if ca_cert == nil
220
+ ca_cert = Apiotics::Portal.ca_certificate
221
+ c = ApioticsSetting.new
222
+ c.key = "ca_cert"
223
+ c.value = ca_cert
224
+ c.save
225
+ else
226
+ ca_cert = ca_cert.value
227
+ end
228
+ ca_cert = OpenSSL::X509::Certificate.new ca_cert
229
+ return ca_cert
230
+ end
231
+
232
+ def do_at_exit
233
+ end
234
+
235
+ end
236
+ end