fog-proxmox 0.14.0 → 0.15.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/workflows/ci.yml +34 -49
- data/.github/workflows/release.yml +38 -0
- data/.rubocop.yml +11 -10
- data/.rubocop_todo.yml +255 -0
- data/CHANGELOG.md +8 -0
- data/README.md +28 -4
- data/Rakefile +8 -35
- data/bin/console +2 -4
- data/bin/setup +2 -4
- data/fog-proxmox.gemspec +7 -5
- data/lib/fog/proxmox/attributes.rb +3 -2
- data/lib/fog/proxmox/auth/token/access_ticket.rb +68 -57
- data/lib/fog/proxmox/auth/token/user_token.rb +79 -66
- data/lib/fog/proxmox/auth/token.rb +66 -60
- data/lib/fog/proxmox/compute/models/disk.rb +6 -1
- data/lib/fog/proxmox/compute/models/disks.rb +1 -1
- data/lib/fog/proxmox/compute/models/interface.rb +1 -1
- data/lib/fog/proxmox/compute/models/interfaces.rb +1 -1
- data/lib/fog/proxmox/compute/models/node.rb +3 -5
- data/lib/fog/proxmox/compute/models/nodes.rb +1 -1
- data/lib/fog/proxmox/compute/models/server.rb +27 -16
- data/lib/fog/proxmox/compute/models/server_config.rb +9 -4
- data/lib/fog/proxmox/compute/models/servers.rb +5 -5
- data/lib/fog/proxmox/compute/models/snapshots.rb +1 -1
- data/lib/fog/proxmox/compute/models/storage.rb +3 -2
- data/lib/fog/proxmox/compute/models/storages.rb +2 -2
- data/lib/fog/proxmox/compute/models/tasks.rb +1 -0
- data/lib/fog/proxmox/compute/models/volume.rb +2 -1
- data/lib/fog/proxmox/compute/models/volumes.rb +2 -2
- data/lib/fog/proxmox/compute/requests/get_server_config.rb +14 -14
- data/lib/fog/proxmox/compute/requests/get_server_status.rb +17 -17
- data/lib/fog/proxmox/compute/requests/log_task.rb +1 -1
- data/lib/fog/proxmox/core.rb +27 -24
- data/lib/fog/proxmox/errors.rb +2 -1
- data/lib/fog/proxmox/hash.rb +0 -2
- data/lib/fog/proxmox/helpers/controller_helper.rb +3 -4
- data/lib/fog/proxmox/helpers/cpu_helper.rb +13 -6
- data/lib/fog/proxmox/helpers/disk_helper.rb +17 -11
- data/lib/fog/proxmox/helpers/ip_helper.rb +21 -20
- data/lib/fog/proxmox/helpers/nic_helper.rb +13 -11
- data/lib/fog/proxmox/identity/models/domain.rb +7 -3
- data/lib/fog/proxmox/identity/models/domain_type.rb +0 -1
- data/lib/fog/proxmox/identity/models/domains.rb +1 -2
- data/lib/fog/proxmox/identity/models/group.rb +4 -2
- data/lib/fog/proxmox/identity/models/groups.rb +1 -1
- data/lib/fog/proxmox/identity/models/permission.rb +5 -4
- data/lib/fog/proxmox/identity/models/permissions.rb +3 -1
- data/lib/fog/proxmox/identity/models/pool.rb +4 -4
- data/lib/fog/proxmox/identity/models/pools.rb +4 -4
- data/lib/fog/proxmox/identity/models/role.rb +1 -1
- data/lib/fog/proxmox/identity/models/roles.rb +1 -1
- data/lib/fog/proxmox/identity/models/token.rb +4 -3
- data/lib/fog/proxmox/identity/models/token_info.rb +2 -2
- data/lib/fog/proxmox/identity/models/tokens.rb +9 -13
- data/lib/fog/proxmox/identity/models/user.rb +1 -2
- data/lib/fog/proxmox/identity/models/users.rb +1 -1
- data/lib/fog/proxmox/identity/requests/get_user.rb +1 -0
- data/lib/fog/proxmox/identity/requests/list_user_permissions.rb +1 -1
- data/lib/fog/proxmox/network/models/networks.rb +1 -1
- data/lib/fog/proxmox/network/models/node.rb +1 -0
- data/lib/fog/proxmox/network/models/nodes.rb +1 -1
- data/lib/fog/proxmox/string.rb +4 -3
- data/lib/fog/proxmox/version.rb +1 -1
- data/lib/fog/proxmox.rb +1 -3
- data/spec/hash_spec.rb +2 -1
- data/spec/helpers/controller_helper_spec.rb +135 -123
- data/spec/helpers/cpu_helper_spec.rb +58 -53
- data/spec/helpers/disk_helper_spec.rb +104 -54
- data/spec/helpers/ip_helper_spec.rb +155 -138
- data/spec/helpers/nic_helper_spec.rb +28 -19
- data/spec/identity_spec.rb +86 -74
- data/spec/proxmox_vcr.rb +2 -2
- data/tasks/audit.rake +25 -0
- data/tasks/lint.rake +22 -0
- data/tasks/test.rake +65 -0
- metadata +62 -47
- data/.codeclimate.yml +0 -14
@@ -28,7 +28,7 @@ module Fog
|
|
28
28
|
attribute :members
|
29
29
|
|
30
30
|
def save(options = {})
|
31
|
-
service.create_group((attributes.reject { |attribute| [
|
31
|
+
service.create_group((attributes.reject { |attribute| %i[users members].include? attribute }).merge(options))
|
32
32
|
reload
|
33
33
|
end
|
34
34
|
|
@@ -40,7 +40,9 @@ module Fog
|
|
40
40
|
|
41
41
|
def update
|
42
42
|
requires :groupid
|
43
|
-
service.update_group(identity, attributes.reject
|
43
|
+
service.update_group(identity, attributes.reject do |attribute|
|
44
|
+
%i[groupid users members].include? attribute
|
45
|
+
end)
|
44
46
|
reload
|
45
47
|
end
|
46
48
|
end
|
@@ -40,17 +40,18 @@ module Fog
|
|
40
40
|
|
41
41
|
def initialize_roles(new_attributes = {})
|
42
42
|
roles = new_attributes.delete(:roleid)
|
43
|
-
new_attributes.store(:roles, roles)
|
43
|
+
new_attributes.store(:roles, roles)
|
44
44
|
end
|
45
45
|
|
46
46
|
def initialize_ugid(new_attributes = {})
|
47
47
|
ugs = new_attributes.delete(:ugid)
|
48
|
-
|
48
|
+
case type
|
49
|
+
when 'user'
|
49
50
|
new_attributes.store(:users, ugs)
|
50
|
-
|
51
|
+
when 'group'
|
51
52
|
new_attributes.store(:groups, ugs)
|
52
53
|
end
|
53
|
-
new_attributes.delete(:type)
|
54
|
+
new_attributes.delete(:type)
|
54
55
|
end
|
55
56
|
|
56
57
|
def to_update
|
@@ -31,7 +31,9 @@ module Fog
|
|
31
31
|
end
|
32
32
|
|
33
33
|
def get(type, path, roleid, ugid)
|
34
|
-
all.find
|
34
|
+
all.find do |permission|
|
35
|
+
permission.type == type && permission.path == path && permission.roleid == roleid && permission.ugid == ugid
|
36
|
+
end
|
35
37
|
end
|
36
38
|
|
37
39
|
def destroy(permission_hash)
|
@@ -67,14 +67,14 @@ module Fog
|
|
67
67
|
end
|
68
68
|
|
69
69
|
def has_server?(vmid)
|
70
|
-
has?(
|
70
|
+
has?('vmid', vmid)
|
71
71
|
end
|
72
72
|
|
73
73
|
def has_storage?(storage)
|
74
|
-
has?(
|
74
|
+
has?('storage', storage)
|
75
75
|
end
|
76
|
-
|
77
|
-
private
|
76
|
+
|
77
|
+
private
|
78
78
|
|
79
79
|
def has?(key, vmid)
|
80
80
|
result = false
|
@@ -27,13 +27,13 @@ module Fog
|
|
27
27
|
model Fog::Proxmox::Identity::Pool
|
28
28
|
|
29
29
|
def all
|
30
|
-
pools_with_members =
|
31
|
-
service.list_pools.each { |pool| pools_with_members.push(pool.merge(service.get_pool(pool[
|
30
|
+
pools_with_members = []
|
31
|
+
service.list_pools.each { |pool| pools_with_members.push(pool.merge(service.get_pool(pool['poolid']))) }
|
32
32
|
load pools_with_members
|
33
33
|
end
|
34
|
-
|
34
|
+
|
35
35
|
def get(id)
|
36
|
-
all.find { |pool| pool.identity
|
36
|
+
all.find { |pool| pool.identity == id }
|
37
37
|
end
|
38
38
|
|
39
39
|
def destroy(id)
|
@@ -39,7 +39,7 @@ module Fog
|
|
39
39
|
|
40
40
|
def update
|
41
41
|
requires :roleid
|
42
|
-
service.update_role(roleid, attributes.reject { |attribute| [
|
42
|
+
service.update_role(roleid, attributes.reject { |attribute| %i[roleid special].include? attribute })
|
43
43
|
reload
|
44
44
|
end
|
45
45
|
end
|
@@ -40,10 +40,9 @@ module Fog
|
|
40
40
|
super(new_attributes)
|
41
41
|
end
|
42
42
|
|
43
|
-
|
44
43
|
def save(options = {})
|
45
44
|
requires :tokenid, :userid
|
46
|
-
token_hash = (attributes.reject { |attribute| [
|
45
|
+
token_hash = (attributes.reject { |attribute| %i[userid tokenid info].include? attribute }).merge(options)
|
47
46
|
service.create_token(userid, tokenid, token_hash)
|
48
47
|
reload
|
49
48
|
end
|
@@ -56,7 +55,9 @@ module Fog
|
|
56
55
|
|
57
56
|
def update
|
58
57
|
requires :tokenid, :userid
|
59
|
-
service.update_token(userid, tokenid, attributes.reject
|
58
|
+
service.update_token(userid, tokenid, attributes.reject do |attribute|
|
59
|
+
%i[userid tokenid info].include? attribute
|
60
|
+
end)
|
60
61
|
reload
|
61
62
|
end
|
62
63
|
|
@@ -24,9 +24,9 @@ module Fog
|
|
24
24
|
class TokenInfo < Fog::Model
|
25
25
|
identity :tokenid
|
26
26
|
identity :userid
|
27
|
-
attribute
|
27
|
+
attribute :fulltokenid
|
28
28
|
attribute :info
|
29
|
-
attribute :value
|
29
|
+
attribute :value
|
30
30
|
|
31
31
|
def initialize(new_attributes = {})
|
32
32
|
prepare_service_value(new_attributes)
|
@@ -32,24 +32,20 @@ module Fog
|
|
32
32
|
end
|
33
33
|
|
34
34
|
def get(tokenid)
|
35
|
-
all.find { |token| token.tokenid
|
35
|
+
all.find { |token| token.tokenid == tokenid && token.userid == userid }
|
36
36
|
end
|
37
37
|
|
38
|
-
def all(
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
else
|
45
|
-
raise error
|
46
|
-
end
|
47
|
-
end
|
38
|
+
def all(_options = {})
|
39
|
+
load service.list_tokens(userid)
|
40
|
+
rescue Excon::Error::InternalServerError => e
|
41
|
+
raise e unless e.response.status_line.include? 'no such user'
|
42
|
+
|
43
|
+
[]
|
48
44
|
end
|
49
45
|
|
50
46
|
def create(new_attributes = {})
|
51
|
-
object = new(new_attributes.select { |key, _value| [
|
52
|
-
object.save(new_attributes.reject { |key, _value| [
|
47
|
+
object = new(new_attributes.select { |key, _value| %i[userid tokenid].include? key.to_sym })
|
48
|
+
object.save(new_attributes.reject { |key, _value| %i[userid tokenid].include? key.to_sym })
|
53
49
|
object
|
54
50
|
end
|
55
51
|
end
|
@@ -57,7 +57,7 @@ module Fog
|
|
57
57
|
|
58
58
|
def update
|
59
59
|
requires :userid
|
60
|
-
service.update_user(userid, attributes.reject { |attribute| [
|
60
|
+
service.update_user(userid, attributes.reject { |attribute| %i[userid tokens].include? attribute })
|
61
61
|
reload
|
62
62
|
end
|
63
63
|
|
@@ -76,7 +76,6 @@ module Fog
|
|
76
76
|
def initialize_tokens
|
77
77
|
attributes[:tokens] = Fog::Proxmox::Identity::Tokens.new(service: service, userid: userid)
|
78
78
|
end
|
79
|
-
|
80
79
|
end
|
81
80
|
end
|
82
81
|
end
|
data/lib/fog/proxmox/string.rb
CHANGED
@@ -22,9 +22,10 @@ module Fog
|
|
22
22
|
# module String mixins
|
23
23
|
module String
|
24
24
|
def self.to_boolean(text)
|
25
|
-
return true if text == true
|
26
|
-
return false if text == false
|
27
|
-
|
25
|
+
return true if text == true || text =~ (/(true|t|yes|y|1)$/i)
|
26
|
+
return false if text == false || text.empty? || text =~ (/(false|f|no|n|0)$/i)
|
27
|
+
|
28
|
+
raise ArgumentError, "invalid value for Boolean: \"#{text}\""
|
28
29
|
end
|
29
30
|
end
|
30
31
|
end
|
data/lib/fog/proxmox/version.rb
CHANGED
data/lib/fog/proxmox.rb
CHANGED
@@ -24,7 +24,6 @@ require 'fog/json'
|
|
24
24
|
module Fog
|
25
25
|
# Proxmox module
|
26
26
|
module Proxmox
|
27
|
-
|
28
27
|
require 'fog/proxmox/auth/token'
|
29
28
|
|
30
29
|
autoload :Core, 'fog/proxmox/core'
|
@@ -35,7 +34,7 @@ module Fog
|
|
35
34
|
autoload :Network, 'fog/proxmox/network'
|
36
35
|
|
37
36
|
extend Fog::Provider
|
38
|
-
|
37
|
+
|
39
38
|
service(:identity, 'Identity')
|
40
39
|
service(:compute, 'Compute')
|
41
40
|
service(:storage, 'Storage')
|
@@ -50,6 +49,5 @@ module Fog
|
|
50
49
|
def self.clear_token_cache
|
51
50
|
Fog::Proxmox.token_cache = {}
|
52
51
|
end
|
53
|
-
|
54
52
|
end
|
55
53
|
end
|
data/spec/hash_spec.rb
CHANGED
@@ -30,7 +30,8 @@ describe Fog::Proxmox::Hash do
|
|
30
30
|
|
31
31
|
describe '#flatten' do
|
32
32
|
it 'returns string net_vm' do
|
33
|
-
assert_equal 'net0: virtio=66:89:C5:59:AA:96,bridge=vmbr0,firewall=1,link_down=1,queues=1,rate=1,tag=1',
|
33
|
+
assert_equal 'net0: virtio=66:89:C5:59:AA:96,bridge=vmbr0,firewall=1,link_down=1,queues=1,rate=1,tag=1',
|
34
|
+
Fog::Proxmox::Hash.flatten(net_vm)
|
34
35
|
end
|
35
36
|
end
|
36
37
|
|
@@ -20,126 +20,138 @@
|
|
20
20
|
require 'spec_helper'
|
21
21
|
require 'fog/proxmox/helpers/controller_helper'
|
22
22
|
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
23
|
+
describe Fog::Proxmox::ControllerHelper do
|
24
|
+
let(:net) do
|
25
|
+
{ net0: 'virtio=66:89:C5:59:AA:96,bridge=vmbr0,firewall=1,link_down=1,queues=1,rate=1,tag=1' }
|
26
|
+
end
|
27
|
+
let(:net_no_options) do
|
28
|
+
{ net0: 'virtio=66:89:C5:59:AA:96' }
|
29
|
+
end
|
30
|
+
let(:net_lxc) do
|
31
|
+
{ net0: 'virtio=66:89:C5:59:AA:96,bridge=vmbr0,firewall=1,link_down=1,queues=1,rate=1,tag=1,ip=192.168.56.100/31,ip6=2001:0000:1234:0000:0000:C1C0:ABCD:0876/31' }
|
32
|
+
end
|
33
|
+
let(:scsi) do
|
34
|
+
{ scsi10: 'local-lvm:1,cache=none' }
|
35
|
+
end
|
36
|
+
let(:cdrom) do
|
37
|
+
{ ide2: 'none,media=cdrom' }
|
38
|
+
end
|
39
|
+
let(:mp) do
|
40
|
+
{ mp0: 'local-lvm:1,mp=/opt/path' }
|
41
|
+
end
|
42
|
+
let(:rootfs) do
|
43
|
+
{ rootfs: 'local-lvm:1' }
|
44
|
+
end
|
45
|
+
|
46
|
+
describe '#extract' do
|
47
|
+
it 'returns bridge' do
|
48
|
+
bridge = Fog::Proxmox::ControllerHelper.extract('bridge', net[:net0])
|
49
|
+
assert_equal 'vmbr0', bridge
|
50
|
+
end
|
51
|
+
|
52
|
+
it 'returns nil' do
|
53
|
+
bridge = Fog::Proxmox::ControllerHelper.extract('bridge', net_no_options[:net0])
|
54
|
+
assert !bridge
|
55
|
+
end
|
56
|
+
|
57
|
+
it 'returns firewall' do
|
58
|
+
firewall = Fog::Proxmox::ControllerHelper.extract('firewall', net[:net0])
|
59
|
+
assert_equal '1', firewall
|
60
|
+
end
|
61
|
+
|
62
|
+
it 'returns cache' do
|
63
|
+
cache = Fog::Proxmox::ControllerHelper.extract('cache', scsi[:scsi10])
|
64
|
+
assert_equal 'none', cache
|
65
|
+
end
|
66
|
+
|
67
|
+
it 'returns mp' do
|
68
|
+
path = Fog::Proxmox::ControllerHelper.extract('mp', mp[:mp0])
|
69
|
+
assert_equal '/opt/path', path
|
70
|
+
end
|
71
|
+
|
72
|
+
it 'returns cidr ip' do
|
73
|
+
path = Fog::Proxmox::ControllerHelper.extract('ip', net_lxc[:net0])
|
74
|
+
assert_equal '192.168.56.100/31', path
|
75
|
+
end
|
76
|
+
|
77
|
+
it 'returns cidr ip6' do
|
78
|
+
path = Fog::Proxmox::ControllerHelper.extract('ip6', net_lxc[:net0])
|
79
|
+
assert_equal '2001:0000:1234:0000:0000:C1C0:ABCD:0876/31', path
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
describe '#extract_index' do
|
84
|
+
it 'net0 returns 0' do
|
85
|
+
index = Fog::Proxmox::ControllerHelper.extract_index('net', :net0)
|
86
|
+
assert index == 0
|
87
|
+
end
|
88
|
+
|
89
|
+
it 'scsi10 returns 10' do
|
90
|
+
index = Fog::Proxmox::ControllerHelper.extract_index('scsi', :scsi10)
|
91
|
+
assert index == 10
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
describe '#last_index' do
|
96
|
+
it 'returns -1' do
|
97
|
+
last = Fog::Proxmox::ControllerHelper.last_index('net', {})
|
98
|
+
assert last == -1
|
99
|
+
end
|
100
|
+
|
101
|
+
it 'returns 0' do
|
102
|
+
last = Fog::Proxmox::ControllerHelper.last_index('net', net)
|
103
|
+
assert last == 0
|
104
|
+
end
|
105
|
+
|
106
|
+
it 'returns 10' do
|
107
|
+
last = Fog::Proxmox::ControllerHelper.last_index('scsi', scsi)
|
108
|
+
assert last == 10
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
describe '#valid?' do
|
113
|
+
it 'returns true' do
|
114
|
+
assert Fog::Proxmox::ControllerHelper.valid?('net', 'net0')
|
115
|
+
end
|
116
|
+
|
117
|
+
it 'returns false' do
|
118
|
+
assert !Fog::Proxmox::ControllerHelper.valid?('net', 'sdfdsf')
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
describe '#select' do
|
123
|
+
it 'returns scsi10' do
|
124
|
+
controllers = Fog::Proxmox::ControllerHelper.select(scsi, 'scsi')
|
125
|
+
assert controllers.has_key?(:scsi10)
|
126
|
+
assert controllers.has_value?(scsi[:scsi10])
|
127
|
+
end
|
128
|
+
|
129
|
+
it 'returns empty' do
|
130
|
+
controllers = Fog::Proxmox::ControllerHelper.select(net, 'scsi')
|
131
|
+
assert controllers.empty?
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
describe '#collect_controllers' do
|
136
|
+
it 'returns scsi0 and ide2' do
|
137
|
+
controllers = Fog::Proxmox::ControllerHelper.collect_controllers(scsi.merge(cdrom))
|
138
|
+
assert controllers.has_key?(:scsi10)
|
139
|
+
assert controllers.has_value?(scsi[:scsi10])
|
140
|
+
assert controllers.has_key?(:ide2)
|
141
|
+
assert controllers.has_value?(cdrom[:ide2])
|
142
|
+
end
|
143
|
+
|
144
|
+
it 'returns rootfs and mp0' do
|
145
|
+
controllers = Fog::Proxmox::ControllerHelper.collect_controllers(rootfs.merge(mp))
|
146
|
+
assert controllers.has_key?(:mp0)
|
147
|
+
assert controllers.has_value?(mp[:mp0])
|
148
|
+
assert controllers.has_key?(:rootfs)
|
149
|
+
assert controllers.has_value?(rootfs[:rootfs])
|
150
|
+
end
|
151
|
+
|
152
|
+
it 'returns empty' do
|
153
|
+
controllers = Fog::Proxmox::ControllerHelper.collect_controllers(net)
|
154
|
+
assert controllers.empty?
|
155
|
+
end
|
156
|
+
end
|
157
|
+
end
|