imperituroard 0.5.5 → 1.0.1
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/imperituroard.gemspec +3 -0
- data/lib/.DS_Store +0 -0
- data/lib/imperituroard.rb +97 -71
- data/lib/imperituroard/.DS_Store +0 -0
- data/lib/imperituroard/platforms/cps/cps_multiple_thread_req.rb +0 -0
- data/lib/imperituroard/platforms/staros/get_data_ssh.rb +54 -0
- data/lib/imperituroard/platforms/staros/staros_automation.rb +95 -0
- data/lib/imperituroard/platforms/staros/staros_automation_fun.rb +37 -0
- data/lib/imperituroard/platforms/staros/staros_parser.rb +141 -0
- data/lib/imperituroard/projects/dns.rb +1 -1
- data/lib/imperituroard/projects/iot.rb +191 -30
- data/lib/imperituroard/projects/iot/hua_oceanconnect_adapter.rb +22 -8
- data/lib/imperituroard/projects/iot/mongoconnector.rb +177 -2
- data/lib/imperituroard/projects/mhub.rb +2 -0
- data/lib/imperituroard/projects/mhub/infobip.rb +1 -1
- data/lib/imperituroard/projects/mhub/sk.rb +171 -157
- data/lib/imperituroard/projects/mhub/subs/dabrab/dabrab_proced.rb +124 -12
- data/lib/imperituroard/projects/ukaz60.rb +140 -0
- data/lib/imperituroard/projects/{dns/ukaz60 → ukaz60}/belgim.rb +48 -9
- data/lib/imperituroard/projects/{dns/ukaz60 → ukaz60}/dns.rb +0 -0
- data/lib/imperituroard/projects/{dns/ukaz60 → ukaz60}/dns_update.rb +0 -0
- data/lib/imperituroard/projects/{dns/ukaz60 → ukaz60}/fortigate.rb +43 -4
- data/lib/imperituroard/projects/ukaz60/staros.rb +141 -0
- data/lib/imperituroard/projects/{dns/ukaz60 → ukaz60}/ukaz60_add_func.rb +159 -4
- data/lib/imperituroard/version.rb +1 -1
- metadata +28 -8
- data/lib/imperituroard/projects/dns/ukaz60.rb +0 -87
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 7f4803bd76c090876b654425c6a5a76237ce07da
|
4
|
+
data.tar.gz: afc8920cded7afea1bab18ebc83ab992915f1329
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: fa6fd8bb0af774f0acd9a01cd74bb1851f5c437eb06c981a86767d1c0151261f3b62f4bba1624d835bc2902147229d2828f9a1ff8291134ada8786cc3a8acebc
|
7
|
+
data.tar.gz: 19874621621215547e23187b1643f3422d44228d071e357fada3b91e7f0cb35f76e4df8c4b5c5c28bd6194a06f9a9c8f17106a3bc19b6ee5e4c5e0871a7b3ced
|
data/imperituroard.gemspec
CHANGED
@@ -10,6 +10,7 @@ Gem::Specification.new do |spec|
|
|
10
10
|
spec.description = %q{Gem from imperituroard for different actions}
|
11
11
|
spec.homepage = "https://rubygems.org/"
|
12
12
|
spec.license = "MIT"
|
13
|
+
|
13
14
|
spec.required_ruby_version = Gem::Requirement.new(">= 2.3.0")
|
14
15
|
|
15
16
|
spec.metadata["allowed_push_host"] = "https://rubygems.org/"
|
@@ -51,4 +52,6 @@ Gem::Specification.new do |spec|
|
|
51
52
|
spec.add_dependency "simpleidn", "0.1.1"
|
52
53
|
spec.add_dependency "net-scp", "3.0.0"
|
53
54
|
|
55
|
+
spec.add_dependency "rails", "5.0.7.2"
|
56
|
+
|
54
57
|
end
|
data/lib/.DS_Store
CHANGED
Binary file
|
data/lib/imperituroard.rb
CHANGED
@@ -24,7 +24,7 @@ require 'imperituroard/projects/iot'
|
|
24
24
|
require 'imperituroard/projects/dns'
|
25
25
|
require 'imperituroard/platforms/cps/qps_connector'
|
26
26
|
require 'imperituroard/platforms/public/telegram'
|
27
|
-
require 'imperituroard/projects/
|
27
|
+
require 'imperituroard/projects/ukaz60'
|
28
28
|
|
29
29
|
require 'json'
|
30
30
|
require 'ipaddr'
|
@@ -107,42 +107,47 @@ class Pipam
|
|
107
107
|
end
|
108
108
|
|
109
109
|
class Iot
|
110
|
-
attr_accessor :
|
111
|
-
:mongoport,
|
112
|
-
:iotip,
|
113
|
-
:mongo_database,
|
114
|
-
:iotplatform_ip,
|
115
|
-
:iotplatform_port,
|
116
|
-
:cert_path,
|
117
|
-
:key_path,
|
110
|
+
attr_accessor :param_all,
|
118
111
|
:mongo_client,
|
119
112
|
:add_functions_connector,
|
120
|
-
|
121
|
-
|
113
|
+
#:real_ip, #real ip address of procedure caller
|
114
|
+
#:remote_ip, #ip address of balancer
|
122
115
|
:hua_aceanconnect_connector,
|
123
116
|
:internal_func,
|
124
117
|
:iot_connector
|
125
118
|
|
126
|
-
def initialize(mongoip,
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
119
|
+
def initialize(mongoip,
|
120
|
+
mongoport,
|
121
|
+
iotip,
|
122
|
+
mongo_database,
|
123
|
+
iotplatform_ip,
|
124
|
+
iotplatform_port,
|
125
|
+
cert_path,
|
126
|
+
key_path,
|
127
|
+
telegram_api_url,
|
128
|
+
telegram_chat_id
|
129
|
+
#real_ip,
|
130
|
+
#remote_ip
|
131
|
+
)
|
132
|
+
@param_all = {:mongoip => mongoip,
|
133
|
+
:mongoport => mongoport,
|
134
|
+
:iotip => iotip,
|
135
|
+
:mongo_database => mongo_database,
|
136
|
+
:iotplatform_ip => iotplatform_ip,
|
137
|
+
:cert_path => cert_path,
|
138
|
+
:key_path => key_path
|
139
|
+
}
|
140
|
+
@mongo_client = MongoIot.new(param_all[:mongoip], param_all[:mongoport], param_all[:mongo_database])
|
137
141
|
@add_functions_connector = AdditionalFunc.new(telegram_api_url, telegram_chat_id)
|
138
|
-
|
139
|
-
|
140
|
-
@hua_aceanconnect_connector = HuaIot.new(iotplatform_ip, iotplatform_port, cert_path, key_path)
|
142
|
+
#@real_ip = real_ip
|
143
|
+
#@remote_ip = remote_ip
|
144
|
+
@hua_aceanconnect_connector = HuaIot.new(iotplatform_ip, iotplatform_port, cert_path, key_path, param_all[:mongoip], param_all[:mongoport], param_all[:mongo_database])
|
141
145
|
@internal_func = InternalFunc.new
|
142
|
-
@iot_connector = IotFunctions_2.new(mongoip, mongoport, iotip, mongo_database,
|
143
|
-
iotplatform_ip, iotplatform_port, cert_path, key_path, telegram_api_url, telegram_chat_id
|
146
|
+
@iot_connector = IotFunctions_2.new(param_all[:mongoip], param_all[:mongoport], iotip, param_all[:mongo_database],
|
147
|
+
iotplatform_ip, iotplatform_port, cert_path, key_path, telegram_api_url, telegram_chat_id)
|
144
148
|
end
|
145
149
|
|
150
|
+
|
146
151
|
#error list
|
147
152
|
|
148
153
|
#:code => 507, :result => "Unknown SDK error"
|
@@ -157,8 +162,8 @@ class Iot
|
|
157
162
|
#massive commands
|
158
163
|
#+++
|
159
164
|
#iot logic added
|
160
|
-
def add_device_to_profile(login, imei_list)
|
161
|
-
iot_connector.add_device_to_prof_2(login, imei_list)
|
165
|
+
def add_device_to_profile(login, imei_list, real_ip, remote_ip)
|
166
|
+
iot_connector.add_device_to_prof_2(login, imei_list, real_ip, remote_ip)
|
162
167
|
end
|
163
168
|
|
164
169
|
|
@@ -169,8 +174,8 @@ class Iot
|
|
169
174
|
# imei
|
170
175
|
# imei_list =[41234,23452345,132412]
|
171
176
|
#++
|
172
|
-
def device_find(login, imei_list)
|
173
|
-
iot_connector.device_find_2(login, imei_list)
|
177
|
+
def device_find(login, imei_list, real_ip, remote_ip)
|
178
|
+
iot_connector.device_find_2(login, imei_list, real_ip, remote_ip)
|
174
179
|
end
|
175
180
|
|
176
181
|
#!3 device modify, change imei
|
@@ -180,8 +185,8 @@ class Iot
|
|
180
185
|
#massive commands
|
181
186
|
#im_list = [{"imei_old"=>7967843245667, "imei_new"=>7967843245665}]
|
182
187
|
#++
|
183
|
-
def imei_replace(login, im_list)
|
184
|
-
iot_connector.imei_replace_2(login, im_list)
|
188
|
+
def imei_replace(login, im_list, real_ip, remote_ip)
|
189
|
+
iot_connector.imei_replace_2(login, im_list, real_ip, remote_ip)
|
185
190
|
end
|
186
191
|
|
187
192
|
#!!4 remove device
|
@@ -192,8 +197,8 @@ class Iot
|
|
192
197
|
#login="test"
|
193
198
|
#+++
|
194
199
|
#IOT logic added
|
195
|
-
def device_remove(login, imei)
|
196
|
-
iot_connector.device_remove_2(login, imei)
|
200
|
+
def device_remove(login, imei, real_ip, remote_ip)
|
201
|
+
iot_connector.device_remove_2(login, imei, real_ip, remote_ip)
|
197
202
|
end
|
198
203
|
|
199
204
|
|
@@ -204,8 +209,8 @@ class Iot
|
|
204
209
|
#newdevice_list=[{:imei=>7967843245665, :address=>"Golubeva51"}]
|
205
210
|
#+++
|
206
211
|
#iot platform integration completed
|
207
|
-
def device_add_address(login, newdevice_list)
|
208
|
-
iot_connector.device_add_address_2(login, newdevice_list)
|
212
|
+
def device_add_address(login, newdevice_list, real_ip, remote_ip)
|
213
|
+
iot_connector.device_add_address_2(login, newdevice_list, real_ip, remote_ip)
|
209
214
|
end
|
210
215
|
|
211
216
|
#6 add service by SPA
|
@@ -215,38 +220,42 @@ class Iot
|
|
215
220
|
#msisdn
|
216
221
|
#newdevice_list=[{:imei=>7967843245665, :attributes=>{:address=>"Golubeva51", :profile=>"wqeqcqeqwev", :msisdn=>375298766719, :imsi=>25702858586756875}}]
|
217
222
|
#+
|
218
|
-
def add_service(login, device_list)
|
219
|
-
iot_connector.add_service_2(login, device_list)
|
223
|
+
def add_service(login, device_list, real_ip, remote_ip)
|
224
|
+
iot_connector.add_service_2(login, device_list, real_ip, remote_ip)
|
220
225
|
end
|
221
226
|
|
222
|
-
|
223
227
|
#7 procedure for subscriber autorization
|
224
|
-
def autorize_subscriber(login, password)
|
225
|
-
iot_connector.autorize_subscriber_2(login, password)
|
228
|
+
def autorize_subscriber(login, password, real_ip, remote_ip)
|
229
|
+
iot_connector.autorize_subscriber_2(login, password, real_ip, remote_ip)
|
226
230
|
end
|
227
231
|
|
228
|
-
|
229
232
|
#8 get available profiles for login
|
230
|
-
def get_available_profiles(login)
|
231
|
-
iot_connector.get_available_prof_2(login)
|
233
|
+
def get_available_profiles(login, real_ip, remote_ip)
|
234
|
+
iot_connector.get_available_prof_2(login, real_ip, remote_ip)
|
232
235
|
end
|
233
236
|
|
234
|
-
|
235
237
|
#9 get available types for login
|
236
|
-
def get_available_types(login)
|
237
|
-
iot_connector.get_available_types_2(login)
|
238
|
+
def get_available_types(login, real_ip, remote_ip)
|
239
|
+
iot_connector.get_available_types_2(login, real_ip, remote_ip)
|
238
240
|
end
|
239
241
|
|
240
|
-
|
241
242
|
#10 get info for device type
|
242
|
-
def get_info_data_type(type)
|
243
|
-
iot_connector.get_info_data_type_2(type)
|
243
|
+
def get_info_data_type(type, real_ip, remote_ip)
|
244
|
+
iot_connector.get_info_data_type_2(type, real_ip, remote_ip)
|
244
245
|
end
|
245
246
|
|
246
|
-
|
247
247
|
#11 get info for profile
|
248
|
-
def get_info_data_profile(profile)
|
249
|
-
iot_connector.get_info_data_profile_2(profile)
|
248
|
+
def get_info_data_profile(profile, real_ip, remote_ip)
|
249
|
+
iot_connector.get_info_data_profile_2(profile, real_ip, remote_ip)
|
250
|
+
end
|
251
|
+
|
252
|
+
#13 replace data for imei. Replace Type and Profile
|
253
|
+
#new procedure 2020-06-06
|
254
|
+
#params =
|
255
|
+
# {"autorization"=>{"login"=>"test", "token"=>"token"}, "datalist"=>[{"imei"=>"524523654366", "new_profile"=>"3", "new_type"=>"1000001"}, {"imei"=>"53623413423", "new_profile"=>"1", "new_type"=>"1000003"}]}
|
256
|
+
# answer = {:code=>200, :result=>"Success", :body=>{:processednum=>1, :failednum=>1, :deviceserr=>[{:failedimei=>524523654366}]}}
|
257
|
+
def set_data_replace(soapgw_params, real_ip, remote_ip)
|
258
|
+
iot_connector.set_data_replace_2(soapgw_params, real_ip, remote_ip)
|
250
259
|
end
|
251
260
|
|
252
261
|
|
@@ -255,10 +264,9 @@ class Iot
|
|
255
264
|
end
|
256
265
|
|
257
266
|
def test111
|
258
|
-
iot_connector.
|
267
|
+
iot_connector.test1278493
|
259
268
|
end
|
260
269
|
|
261
|
-
|
262
270
|
end
|
263
271
|
|
264
272
|
|
@@ -345,12 +353,24 @@ class Ukaz60Automation_2
|
|
345
353
|
belgim_login,
|
346
354
|
belgim_password,
|
347
355
|
belgim_url,
|
348
|
-
|
349
|
-
|
356
|
+
|
357
|
+
###########for fortigate############
|
358
|
+
ansible_tmp_folder, #folder on fortigate ansible server where url list uploaded
|
359
|
+
local_tmp_directory, #folder on local mashin where script work
|
350
360
|
ansible_ssh_user,
|
351
361
|
ansible_ssh_password,
|
352
|
-
ansible_host,
|
353
|
-
url_backup_dir
|
362
|
+
ansible_host, #fortigate ansible server
|
363
|
+
url_backup_dir,
|
364
|
+
####################################
|
365
|
+
|
366
|
+
#########for STAROS#############
|
367
|
+
staros_login,
|
368
|
+
staros_password,
|
369
|
+
ukaz60_http_groups,
|
370
|
+
ukaz60_https_groups,
|
371
|
+
staros_hosts
|
372
|
+
|
373
|
+
)
|
354
374
|
=begin
|
355
375
|
@ukaz69_dns_func = Dns_2.new(telegram_api_url,
|
356
376
|
telegram_chat_id,
|
@@ -368,20 +388,26 @@ class Ukaz60Automation_2
|
|
368
388
|
=end
|
369
389
|
|
370
390
|
@ukaz60_func = Ukaz60All_2.new(telegram_api_url,
|
371
|
-
|
372
|
-
|
373
|
-
|
374
|
-
|
375
|
-
|
376
|
-
|
377
|
-
|
378
|
-
|
379
|
-
|
380
|
-
|
391
|
+
telegram_chat_id,
|
392
|
+
belgim_login,
|
393
|
+
belgim_password,
|
394
|
+
belgim_url,
|
395
|
+
ansible_tmp_folder,
|
396
|
+
local_tmp_directory,
|
397
|
+
ansible_ssh_user,
|
398
|
+
ansible_ssh_password,
|
399
|
+
ansible_host,
|
400
|
+
url_backup_dir,
|
401
|
+
staros_login,
|
402
|
+
staros_password,
|
403
|
+
ukaz60_http_groups,
|
404
|
+
ukaz60_https_groups,
|
405
|
+
staros_hosts)
|
381
406
|
end
|
382
407
|
|
408
|
+
#1 block all devices by belgim list
|
383
409
|
def no_pasaran
|
384
410
|
ukaz60_func.no_pasaran
|
385
411
|
end
|
386
412
|
|
387
|
-
end
|
413
|
+
end
|
data/lib/imperituroard/.DS_Store
CHANGED
Binary file
|
File without changes
|
@@ -0,0 +1,54 @@
|
|
1
|
+
require 'net/ssh'
|
2
|
+
|
3
|
+
require 'imperituroard/platforms/staros/staros_parser'
|
4
|
+
|
5
|
+
class StarosGet_2
|
6
|
+
|
7
|
+
attr_accessor :staros_parser, :staros_login, :staros_password
|
8
|
+
|
9
|
+
def initialize(staros_login, staros_password)
|
10
|
+
@staros_parser = StarosParser_2.new
|
11
|
+
@staros_login = staros_login
|
12
|
+
@staros_password = staros_password
|
13
|
+
end
|
14
|
+
|
15
|
+
|
16
|
+
#get all active-charging data
|
17
|
+
#host - staros device IP address
|
18
|
+
#return parsed ECS configuration in JSON
|
19
|
+
def get_all_active_charging(host_ip)
|
20
|
+
|
21
|
+
input_data = {:host_ip => host_ip}
|
22
|
+
out_data = {}
|
23
|
+
config_all = []
|
24
|
+
|
25
|
+
begin
|
26
|
+
|
27
|
+
Net::SSH.start(host_ip, staros_login, :password => staros_password) do |ssh|
|
28
|
+
staros_answer = ssh.exec!("show configuration active-charging service all")
|
29
|
+
staros_answer = staros_answer.gsub("#exit", "exit")
|
30
|
+
#staros_answer = staros_answer.gsub("\n", " ")
|
31
|
+
#p staros_answer
|
32
|
+
#host_pool_regexp = /(host-pool.+\s+exit)/
|
33
|
+
#fff = staros_answer.scan(host_pool_regexp)
|
34
|
+
#p fff
|
35
|
+
|
36
|
+
staros_answer = staros_answer.split("\n")
|
37
|
+
for ttt in staros_answer
|
38
|
+
regexp_full_command = /\s+(.+)/
|
39
|
+
group_ruled_ukaz60_9 = ttt.match(regexp_full_command)
|
40
|
+
if group_ruled_ukaz60_9 != nil
|
41
|
+
config_all.push(group_ruled_ukaz60_9[1])
|
42
|
+
else
|
43
|
+
config_all.push(ttt)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
staros_json = staros_parser.ecs_config_parser(config_all)
|
48
|
+
out_data = {:code => 200, :result => "Successfully completed", :parsed_data => staros_json}
|
49
|
+
rescue
|
50
|
+
out_data = {:code => 507, :result => "get_all_active_charging: Unknown SDK error"}
|
51
|
+
end
|
52
|
+
out_data
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,95 @@
|
|
1
|
+
require 'net/ssh'
|
2
|
+
|
3
|
+
staros_thr_1233 = []
|
4
|
+
conf = {}
|
5
|
+
|
6
|
+
staros_hosts = {
|
7
|
+
#5700
|
8
|
+
}
|
9
|
+
|
10
|
+
def printer (config)
|
11
|
+
for gggg1 in config
|
12
|
+
|
13
|
+
p ""
|
14
|
+
p ""
|
15
|
+
p "##{gggg1[0]}"
|
16
|
+
for jjj in gggg1[1]
|
17
|
+
p jjj
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
staros_hosts.each do |ggsn|
|
23
|
+
staros_thr_1233 << Thread.new do
|
24
|
+
|
25
|
+
p ggsn
|
26
|
+
|
27
|
+
begin
|
28
|
+
|
29
|
+
conf[ggsn[0]] = []
|
30
|
+
|
31
|
+
Net::SSH.start(ggsn[1], "ывапывап", :password => "пыапыва") do |ssh|
|
32
|
+
staros_answer = ssh.exec!("show configuration context Ga | grep edr")
|
33
|
+
|
34
|
+
regexp = /file name (\S+) rotation volume (\d+) rotation time \d+ storage-limit \d+ headers edr-format-name compression gzip file-sequence-number rulebase-seq-num/
|
35
|
+
|
36
|
+
ggg = staros_answer.match(regexp)
|
37
|
+
conf[ggsn[0]].push("configure")
|
38
|
+
conf[ggsn[0]].push("context Ga")
|
39
|
+
conf[ggsn[0]].push("edr-module active-charging-service charging")
|
40
|
+
conf[ggsn[0]].push("file name #{ggg[1]} rotation volume 40000000 rotation time 600 storage-limit 536870912 headers edr-format-name compression gzip file-sequence-number rulebase-seq-num")
|
41
|
+
conf[ggsn[0]].push("end")
|
42
|
+
|
43
|
+
#config
|
44
|
+
# staros_answer1 = ssh.exec!("configure")
|
45
|
+
# p staros_answer1
|
46
|
+
# staros_answer2 = ssh.exec!("context Ga")
|
47
|
+
# p staros_answer2
|
48
|
+
# staros_answer3 = ssh.exec!("edr-module active-charging-service charging")
|
49
|
+
# p staros_answer3
|
50
|
+
# staros_answer4 = ssh.exec!("file name #{ggg[1]} rotation volume 40000000 rotation time 600 storage-limit 536870912 headers edr-format-name compression gzip file-sequence-number rulebase-seq-num")
|
51
|
+
# p staros_answer4
|
52
|
+
# staros_answer5 = ssh.exec!("end")
|
53
|
+
# p staros_answer5
|
54
|
+
|
55
|
+
#check
|
56
|
+
staros_answer7 = ssh.exec!("show configuration | grep hostname")
|
57
|
+
reggg = /system hostname (\S+)/
|
58
|
+
hostname_this = staros_answer7.match(reggg)[1]
|
59
|
+
|
60
|
+
staros_answer8 = ssh.exec!("show configuration context Ga | grep edr")
|
61
|
+
regexp2 = /file name (\S+) rotation volume (\d+) rotation time \d+ storage-limit \d+ headers edr-format-name compression gzip file-sequence-number rulebase-seq-num/
|
62
|
+
|
63
|
+
ggg2 = staros_answer8.match(regexp2)
|
64
|
+
|
65
|
+
a1 = ggg2[1].gsub("edr-", "")
|
66
|
+
#p a1
|
67
|
+
#p hostname_this
|
68
|
+
#p ggg2
|
69
|
+
|
70
|
+
if a1 != hostname_this
|
71
|
+
p "incorrect: #{hostname_this}"
|
72
|
+
end
|
73
|
+
|
74
|
+
|
75
|
+
if ggg2[2] == "40000000"
|
76
|
+
else
|
77
|
+
p "incorrect value: #{hostname_this}"
|
78
|
+
end
|
79
|
+
|
80
|
+
end
|
81
|
+
|
82
|
+
rescue
|
83
|
+
|
84
|
+
p "#{ggsn[0]} ERROR"
|
85
|
+
|
86
|
+
end
|
87
|
+
|
88
|
+
end
|
89
|
+
end
|
90
|
+
staros_thr_1233.each(&:join)
|
91
|
+
|
92
|
+
#p conf
|
93
|
+
|
94
|
+
|
95
|
+
#printer(conf)
|