fog-openstack 0.1.5 → 0.1.6

Sign up to get free protection for your applications and to get access to all the features.
Files changed (141) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +1 -0
  3. data/.travis.yml +6 -2
  4. data/README.md +4 -0
  5. data/Rakefile +10 -2
  6. data/fog-openstack.gemspec +2 -3
  7. data/gemfiles/Gemfile-1.9 +1 -1
  8. data/lib/fog/openstack.rb +14 -6
  9. data/lib/fog/openstack/baremetal.rb +1 -1
  10. data/lib/fog/openstack/common.rb +0 -2
  11. data/lib/fog/openstack/compute.rb +78 -75
  12. data/lib/fog/openstack/core.rb +2 -1
  13. data/lib/fog/openstack/docs/compute.md +1 -1
  14. data/lib/fog/openstack/docs/network.md +283 -0
  15. data/lib/fog/openstack/docs/nfv.md +144 -0
  16. data/lib/fog/openstack/examples/compute/block_device_mapping_v2.rb +4 -4
  17. data/lib/fog/openstack/examples/network/network_add_port.rb +21 -0
  18. data/lib/fog/openstack/examples/network/network_rbac.rb +69 -0
  19. data/lib/fog/openstack/identity.rb +14 -22
  20. data/lib/fog/openstack/identity_v2.rb +1 -1
  21. data/lib/fog/openstack/image_v1.rb +13 -28
  22. data/lib/fog/openstack/image_v2.rb +15 -17
  23. data/lib/fog/openstack/introspection.rb +1 -1
  24. data/lib/fog/openstack/metering.rb +1 -1
  25. data/lib/fog/openstack/models/collection.rb +4 -1
  26. data/lib/fog/openstack/models/compute/availability_zone.rb +5 -0
  27. data/lib/fog/openstack/models/compute/flavor.rb +21 -9
  28. data/lib/fog/openstack/models/compute/server.rb +64 -77
  29. data/lib/fog/openstack/models/compute/snapshot.rb +6 -6
  30. data/lib/fog/openstack/models/compute/volume_attachment.rb +15 -0
  31. data/lib/fog/openstack/models/compute/volume_attachments.rb +20 -0
  32. data/lib/fog/openstack/models/identity_v3/projects.rb +1 -1
  33. data/lib/fog/openstack/models/identity_v3/users.rb +2 -2
  34. data/lib/fog/openstack/models/image_v2/image.rb +41 -22
  35. data/lib/fog/openstack/models/network/floating_ip.rb +1 -0
  36. data/lib/fog/openstack/models/network/rbac_policies.rb +33 -0
  37. data/lib/fog/openstack/models/network/rbac_policy.rb +35 -0
  38. data/lib/fog/openstack/models/nfv/vnf.rb +58 -0
  39. data/lib/fog/openstack/models/nfv/vnfd.rb +53 -0
  40. data/lib/fog/openstack/models/nfv/vnfds.rb +28 -0
  41. data/lib/fog/openstack/models/nfv/vnfs.rb +28 -0
  42. data/lib/fog/openstack/models/volume/snapshot.rb +38 -0
  43. data/lib/fog/openstack/models/volume/snapshots.rb +26 -0
  44. data/lib/fog/openstack/models/volume/volume.rb +32 -2
  45. data/lib/fog/openstack/models/volume/volume_type.rb +3 -4
  46. data/lib/fog/openstack/models/volume_v1/snapshot.rb +43 -0
  47. data/lib/fog/openstack/models/volume_v1/snapshots.rb +16 -0
  48. data/lib/fog/openstack/models/volume_v1/volume.rb +12 -2
  49. data/lib/fog/openstack/models/volume_v2/snapshot.rb +43 -0
  50. data/lib/fog/openstack/models/volume_v2/snapshots.rb +16 -0
  51. data/lib/fog/openstack/models/volume_v2/volume.rb +12 -3
  52. data/lib/fog/openstack/network.rb +53 -46
  53. data/lib/fog/openstack/nfv.rb +158 -0
  54. data/lib/fog/openstack/orchestration.rb +1 -1
  55. data/lib/fog/openstack/planning.rb +1 -1
  56. data/lib/fog/openstack/requests/compute/create_server.rb +1 -1
  57. data/lib/fog/openstack/requests/compute/{create_volume_snapshot.rb → create_snapshot.rb} +16 -16
  58. data/lib/fog/openstack/requests/compute/delete_flavor_metadata.rb +30 -0
  59. data/lib/fog/openstack/requests/compute/evacuate_server.rb +8 -7
  60. data/lib/fog/openstack/requests/compute/get_key_pair.rb +49 -0
  61. data/lib/fog/openstack/requests/compute/list_availability_zones.rb +23 -0
  62. data/lib/fog/openstack/requests/compute/list_volume_attachments.rb +31 -0
  63. data/lib/fog/openstack/requests/compute/update_flavor_metadata.rb +33 -0
  64. data/lib/fog/openstack/requests/network/create_rbac_policy.rb +42 -0
  65. data/lib/fog/openstack/requests/network/delete_rbac_policy.rb +28 -0
  66. data/lib/fog/openstack/requests/network/get_rbac_policy.rb +28 -0
  67. data/lib/fog/openstack/requests/network/list_rbac_policies.rb +25 -0
  68. data/lib/fog/openstack/requests/network/update_port.rb +8 -7
  69. data/lib/fog/openstack/requests/network/update_rbac_policy.rb +41 -0
  70. data/lib/fog/openstack/requests/nfv/create_vnf.rb +37 -0
  71. data/lib/fog/openstack/requests/nfv/create_vnfd.rb +35 -0
  72. data/lib/fog/openstack/requests/nfv/delete_vnf.rb +23 -0
  73. data/lib/fog/openstack/requests/nfv/delete_vnfd.rb +23 -0
  74. data/lib/fog/openstack/requests/nfv/get_vnf.rb +24 -0
  75. data/lib/fog/openstack/requests/nfv/get_vnfd.rb +24 -0
  76. data/lib/fog/openstack/requests/nfv/list_vnfds.rb +25 -0
  77. data/lib/fog/openstack/requests/nfv/list_vnfs.rb +25 -0
  78. data/lib/fog/openstack/requests/nfv/update_vnf.rb +35 -0
  79. data/lib/fog/openstack/requests/volume/action.rb +16 -0
  80. data/lib/fog/openstack/requests/volume/create_snapshot.rb +18 -0
  81. data/lib/fog/openstack/requests/volume/delete_metadata.rb +15 -0
  82. data/lib/fog/openstack/requests/volume/delete_snapshot.rb +5 -5
  83. data/lib/fog/openstack/requests/volume/delete_snapshot_metadata.rb +15 -0
  84. data/lib/fog/openstack/requests/volume/replace_metadata.rb +20 -0
  85. data/lib/fog/openstack/requests/volume/update_metadata.rb +20 -0
  86. data/lib/fog/openstack/requests/volume/update_snapshot.rb +37 -0
  87. data/lib/fog/openstack/requests/volume/update_snapshot_metadata.rb +20 -0
  88. data/lib/fog/openstack/requests/volume/update_volume.rb +25 -0
  89. data/lib/fog/openstack/requests/volume_v1/action.rb +2 -0
  90. data/lib/fog/openstack/requests/volume_v1/create_snapshot.rb +45 -0
  91. data/lib/fog/openstack/requests/volume_v1/delete_metadata.rb +2 -0
  92. data/lib/fog/openstack/requests/volume_v1/delete_snapshot_metadata.rb +2 -0
  93. data/lib/fog/openstack/requests/volume_v1/replace_metadata.rb +2 -0
  94. data/lib/fog/openstack/requests/volume_v1/update_metadata.rb +2 -0
  95. data/lib/fog/openstack/requests/volume_v1/update_snapshot.rb +2 -0
  96. data/lib/fog/openstack/requests/volume_v1/update_snapshot_metadata.rb +2 -0
  97. data/lib/fog/openstack/requests/volume_v1/update_volume.rb +2 -0
  98. data/lib/fog/openstack/requests/volume_v2/action.rb +2 -0
  99. data/lib/fog/openstack/requests/volume_v2/create_snapshot.rb +45 -0
  100. data/lib/fog/openstack/requests/volume_v2/delete_metadata.rb +2 -0
  101. data/lib/fog/openstack/requests/volume_v2/delete_snapshot_metadata.rb +2 -0
  102. data/lib/fog/openstack/requests/volume_v2/replace_metadata.rb +2 -0
  103. data/lib/fog/openstack/requests/volume_v2/update_metadata.rb +2 -0
  104. data/lib/fog/openstack/requests/volume_v2/update_snapshot.rb +2 -0
  105. data/lib/fog/openstack/requests/volume_v2/update_snapshot_metadata.rb +2 -0
  106. data/lib/fog/openstack/requests/volume_v2/update_volume.rb +2 -0
  107. data/lib/fog/openstack/storage.rb +1 -1
  108. data/lib/fog/openstack/version.rb +1 -1
  109. data/lib/fog/openstack/volume.rb +1 -1
  110. data/lib/fog/openstack/volume_v1.rb +29 -21
  111. data/lib/fog/openstack/volume_v2.rb +29 -21
  112. data/supported.md +54 -0
  113. data/tests/openstack/models/identity/ec2_credential_tests.rb +1 -1
  114. data/tests/openstack/models/identity/ec2_credentials_tests.rb +1 -1
  115. data/tests/openstack/models/identity/role_tests.rb +8 -3
  116. data/tests/openstack/models/identity/roles_tests.rb +5 -4
  117. data/tests/openstack/models/identity/tenant_tests.rb +7 -4
  118. data/tests/openstack/models/identity/tenants_tests.rb +10 -5
  119. data/tests/openstack/models/identity/user_tests.rb +4 -3
  120. data/tests/openstack/models/identity/users_tests.rb +10 -6
  121. data/tests/openstack/models/nfv/vnf_tests.rb +35 -0
  122. data/tests/openstack/models/nfv/vnfd_tests.rb +23 -0
  123. data/tests/openstack/models/nfv/vnfds_tests.rb +31 -0
  124. data/tests/openstack/models/nfv/vnfs_tests.rb +38 -0
  125. data/tests/openstack/requests/identity/ec2_credentials_tests.rb +6 -9
  126. data/tests/openstack/requests/identity/helper.rb +12 -4
  127. data/tests/openstack/requests/identity/role_tests.rb +13 -10
  128. data/tests/openstack/requests/identity/tenant_tests.rb +10 -9
  129. data/tests/openstack/requests/identity/user_tests.rb +11 -6
  130. data/tests/openstack/requests/image/image_tests.rb +1 -0
  131. data/tests/openstack/requests/nfv/vnf_tests.rb +70 -0
  132. data/tests/openstack/requests/nfv/vnfd_tests.rb +44 -0
  133. metadata +81 -31
  134. data/lib/fog/openstack/requests/volume/create_volume_snapshot.rb +0 -26
  135. data/lib/fog/openstack/requests/volume_v1/create_volume_snapshot.rb +0 -44
  136. data/lib/fog/openstack/requests/volume_v2/create_volume_snapshot.rb +0 -43
  137. data/tests/openstack/authenticate_tests.rb +0 -200
  138. data/tests/openstack/identity_version_tests.rb +0 -25
  139. data/tests/openstack/storage_tests.rb +0 -18
  140. data/tests/openstack/version_tests.rb +0 -55
  141. data/tests/openstack/volume_tests.rb +0 -18
@@ -7,16 +7,16 @@ module Fog
7
7
  class Snapshot < Fog::OpenStack::Model
8
8
  identity :id
9
9
 
10
- attribute :name, :aliases => 'displayName'
11
- attribute :description, :aliases => 'displayDescription'
12
- attribute :volume_id, :aliases => 'volumeId'
10
+ attribute :name, :aliases => 'displayName'
11
+ attribute :description, :aliases => 'displayDescription'
12
+ attribute :volume_id, :aliases => 'volumeId'
13
+ attribute :created_at, :aliases => 'createdAt'
13
14
  attribute :status
14
15
  attribute :size
15
- attribute :created_at, :aliases => 'createdAt'
16
16
 
17
- def save(force=false)
17
+ def save(force = false)
18
18
  requires :volume_id, :name, :description
19
- data = service.create_volume_snapshot(volume_id, name, description, force)
19
+ data = service.create_snapshot(volume_id, name, description, force)
20
20
  merge_attributes(data.body['snapshot'])
21
21
  true
22
22
  end
@@ -0,0 +1,15 @@
1
+ require 'fog/core/model'
2
+
3
+ module Fog
4
+ module Compute
5
+ class OpenStack
6
+ class VolumeAttachment < Fog::Model
7
+ identity :id
8
+
9
+ attribute :serverId
10
+ attribute :volumeId
11
+ attribute :device
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,20 @@
1
+ require 'fog/core/collection'
2
+
3
+ module Fog
4
+ module Compute
5
+ class OpenStack
6
+ class VolumeAttachments < Fog::Collection
7
+ model Fog::Compute::OpenStack::VolumeAttachment
8
+
9
+ def get(server_id)
10
+ if server_id
11
+ puts service.list_volume_attachments(server_id).body
12
+ load(service.list_volume_attachments(server_id).body['volumeAttachments'])
13
+ end
14
+ rescue Fog::Compute::OpenStack::NotFound
15
+ nil
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
@@ -54,7 +54,7 @@ module Fog
54
54
 
55
55
  if service.openstack_cache_ttl > 0
56
56
  @@cache[{:token => service.auth_token, :id => id, :options => options}] = [
57
- top_project, Time.now + service.openstack_cache_tt]
57
+ top_project, Time.now + service.openstack_cache_ttl]
58
58
  end
59
59
  return top_project
60
60
  end
@@ -20,8 +20,8 @@ module Fog
20
20
  user_hash.merge(:service => service))
21
21
  end
22
22
 
23
- def find_by_name(name)
24
- load(service.list_users(:name => name).body['users'])
23
+ def find_by_name(name, options = {})
24
+ load(service.list_users(options.merge(:name => name)).body["users"])
25
25
  end
26
26
 
27
27
  def destroy(id)
@@ -21,7 +21,7 @@ module Fog
21
21
  attribute :self
22
22
  attribute :file
23
23
 
24
- #detailed
24
+ # detailed
25
25
  attribute :min_disk
26
26
  attribute :created_at
27
27
  attribute :updated_at
@@ -40,9 +40,29 @@ module Fog
40
40
  attribute :instance_uuid
41
41
  attribute :user_id
42
42
 
43
+ def method_missing(method_sym, *arguments, &block)
44
+ if attributes.key?(method_sym)
45
+ attributes[method_sym]
46
+ elsif attributes.key?(method_sym.to_s)
47
+ attributes[method_sym.to_s]
48
+ else
49
+ super
50
+ end
51
+ end
52
+
53
+ def respond_to?(method_sym, include_all = false)
54
+ if attributes.key?(method_sym)
55
+ true
56
+ elsif attributes.key?(method_sym.to_s)
57
+ true
58
+ else
59
+ super
60
+ end
61
+ end
62
+
43
63
  def create
44
64
  requires :name
45
- merge_attributes(service.create_image(self.attributes).body)
65
+ merge_attributes(service.create_image(attributes).body)
46
66
  self
47
67
  end
48
68
 
@@ -60,88 +80,87 @@ module Fog
60
80
  attr.each do |key, value|
61
81
  op = (@attributes.keys.include? key) ? 'replace' : 'add'
62
82
  op = 'remove' if value.nil?
63
- json_patch << {:op => op, :path => "/#{key}", :value => value }
83
+ json_patch << {:op => op, :path => "/#{key}", :value => value}
64
84
  end
65
85
  merge_attributes(
66
- service.update_image(self.id, json_patch).body)
86
+ service.update_image(id, json_patch).body)
67
87
  self
68
88
  end
69
89
 
70
90
  def destroy
71
91
  requires :id
72
- service.delete_image(self.id)
92
+ service.delete_image(id)
73
93
  true
74
94
  end
75
95
 
76
96
  def upload_data(io_obj)
77
97
  requires :id
78
- if io_obj.is_a? Hash
79
- service.upload_image(self.id, nil, io_obj)
98
+ if io_obj.kind_of? Hash
99
+ service.upload_image(id, nil, io_obj)
80
100
  else
81
- service.upload_image(self.id, io_obj)
101
+ service.upload_image(id, io_obj)
82
102
  end
83
103
  end
84
104
 
85
- def download_data(params={})
105
+ def download_data(params = {})
86
106
  requires :id
87
- service.download_image(self.id, content_range=params[:content_range], params)
107
+ service.download_image(id, params[:content_range], params)
88
108
  end
89
109
 
90
110
  def reactivate
91
111
  requires :id
92
- service.reactivate_image(self.id)
112
+ service.reactivate_image(id)
93
113
  end
94
114
 
95
115
  def deactivate
96
116
  requires :id
97
- service.deactivate_image(self.id)
117
+ service.deactivate_image(id)
98
118
  end
99
119
 
100
120
  def add_member(member_id)
101
121
  requires :id
102
- service.add_member_to_image(self.id, member_id)
122
+ service.add_member_to_image(id, member_id)
103
123
  end
104
124
 
105
125
  def remove_member(member_id)
106
126
  requires :id
107
- service.remove_member_from_image(self.id, member_id)
127
+ service.remove_member_from_image(id, member_id)
108
128
  end
109
129
 
110
130
  def update_member(member)
111
131
  requires :id
112
- service.update_image_member(self.id, member)
132
+ service.update_image_member(id, member)
113
133
  end
114
134
 
115
135
  def members
116
136
  requires :id
117
- service.get_image_members(self.id).body['members']
137
+ service.get_image_members(id).body['members']
118
138
  end
119
139
 
120
140
  def member(member_id)
121
141
  requires :id
122
- service.get_member_details(self.id, member_id)
142
+ service.get_member_details(id, member_id)
123
143
  end
124
144
 
125
145
  def add_tags(tags)
126
146
  requires :id
127
- tags.each {|tag| add_tag tag}
147
+ tags.each { |tag| add_tag tag }
128
148
  end
129
149
 
130
150
  def add_tag(tag)
131
151
  requires :id
132
- service.add_tag_to_image(self.id, tag)
152
+ service.add_tag_to_image(id, tag)
133
153
  end
134
154
 
135
155
  def remove_tags(tags)
136
156
  requires :id
137
- tags.each {|tag| remove_tag tag}
157
+ tags.each { |tag| remove_tag tag }
138
158
  end
139
159
 
140
160
  def remove_tag(tag)
141
161
  requires :id
142
- service.remove_tag_from_image(self.id, tag)
162
+ service.remove_tag_from_image(id, tag)
143
163
  end
144
-
145
164
  end
146
165
  end
147
166
  end
@@ -9,6 +9,7 @@ module Fog
9
9
  attribute :floating_network_id
10
10
  attribute :port_id
11
11
  attribute :tenant_id
12
+ attribute :router_id
12
13
  attribute :fixed_ip_address
13
14
  attribute :floating_ip_address
14
15
 
@@ -0,0 +1,33 @@
1
+ require 'fog/openstack/models/collection'
2
+ require 'fog/openstack/models/network/rbac_policy'
3
+
4
+ module Fog
5
+ module Network
6
+ class OpenStack
7
+ class RbacPolicies < Fog::OpenStack::Collection
8
+ attribute :filters
9
+
10
+ model Fog::Network::OpenStack::RbacPolicy
11
+
12
+ def initialize(attributes)
13
+ self.filters ||= {}
14
+ super
15
+ end
16
+
17
+ def all(filters_arg = filters)
18
+ filters = filters_arg
19
+ load_response(service.list_rbac_policies(filters), 'rbac_policies')
20
+ end
21
+
22
+ def get(rbac_policy_id)
23
+ if rbac_policy = service.get_rbac_policy(rbac_policy_id).body['rbac_policy']
24
+ new(rbac_policy)
25
+ end
26
+ rescue Fog::Network::OpenStack::NotFound
27
+ nil
28
+ end
29
+ alias_method :find_by_id, :get
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,35 @@
1
+ require 'fog/openstack/models/model'
2
+
3
+ module Fog
4
+ module Network
5
+ class OpenStack
6
+ class RbacPolicy < Fog::OpenStack::Model
7
+ identity :id
8
+
9
+ attribute :object_type
10
+ attribute :object_id
11
+ attribute :tenant_id
12
+ attribute :target_tenant
13
+ attribute :action
14
+
15
+ def create
16
+ requires :object_type, :object_id, :target_tenant, :action
17
+ merge_attributes(service.create_rbac_policy(attributes).body['rbac_policy'])
18
+ self
19
+ end
20
+
21
+ def update
22
+ requires :id, :target_tenant
23
+ merge_attributes(service.update_rbac_policy(id, attributes).body['rbac_policy'])
24
+ self
25
+ end
26
+
27
+ def destroy
28
+ requires :id
29
+ service.delete_rbac_policy(id)
30
+ true
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,58 @@
1
+ require 'fog/openstack/models/model'
2
+
3
+ module Fog
4
+ module NFV
5
+ class OpenStack
6
+ class Vnf < Fog::OpenStack::Model
7
+ identity :id
8
+
9
+ attribute :status
10
+ attribute :name
11
+ attribute :tenant_id
12
+ attribute :instance_id
13
+ attribute :mgmt_url
14
+ attribute :description
15
+ attribute :vnf_attributes
16
+
17
+ # Attributes for create and update
18
+ attribute :vnf
19
+ attribute :auth
20
+
21
+ def create(options = {})
22
+ merge_attributes(service.create_vnf(default_options.merge(options)).body['vnf'])
23
+ self
24
+ end
25
+
26
+ def update(options = {})
27
+ merge_attributes(service.update_vnf(identity, default_options.merge(options)).body['vnf'])
28
+ self
29
+ end
30
+
31
+ def save(options = {})
32
+ identity ? update(options) : create(options)
33
+ end
34
+
35
+ def destroy
36
+ requires :id
37
+ service.delete_vnf(id)
38
+ true
39
+ end
40
+
41
+ def default_options
42
+ {
43
+ :vnf => vnf,
44
+ :auth => auth
45
+ }
46
+ end
47
+
48
+ def vnf_attributes
49
+ attributes['attributes']
50
+ end
51
+
52
+ def ready?
53
+ status == 'ACTIVE'
54
+ end
55
+ end
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,53 @@
1
+ require 'fog/openstack/models/model'
2
+
3
+ module Fog
4
+ module NFV
5
+ class OpenStack
6
+ class Vnfd < Fog::OpenStack::Model
7
+ identity :id
8
+
9
+ attribute :service_types
10
+ attribute :description
11
+ attribute :tenant_id
12
+ attribute :mgmt_driver
13
+ attribute :infra_driver
14
+ attribute :name
15
+ attribute :vnf_attributes
16
+
17
+ # Attributes for create
18
+ attribute :vnfd
19
+ attribute :auth
20
+
21
+ def create(options = {})
22
+ merge_attributes(service.create_vnfd(default_options.merge(options)).body['vnfd'])
23
+ self
24
+ end
25
+
26
+ def update(_options = {})
27
+ raise Fog::OpenStack::Errors::InterfaceNotImplemented.new("Method 'update' is not supported")
28
+ end
29
+
30
+ def save(options = {})
31
+ identity ? update(options) : create(options)
32
+ end
33
+
34
+ def destroy
35
+ requires :id
36
+ service.delete_vnfd(id)
37
+ true
38
+ end
39
+
40
+ def default_options
41
+ {
42
+ :vnfd => vnfd,
43
+ :auth => auth
44
+ }
45
+ end
46
+
47
+ def vnf_attributes
48
+ attributes['attributes']
49
+ end
50
+ end
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,28 @@
1
+ require 'fog/openstack/models/collection'
2
+ require 'fog/openstack/models/nfv/vnfd'
3
+
4
+ module Fog
5
+ module NFV
6
+ class OpenStack
7
+ class Vnfds < Fog::OpenStack::Collection
8
+ model Fog::NFV::OpenStack::Vnfd
9
+
10
+ def all(options = {})
11
+ load_response(service.list_vnfds(options), 'vnfds')
12
+ end
13
+
14
+ def get(uuid)
15
+ data = service.get_vnfd(uuid).body['vnfd']
16
+ new(data)
17
+ rescue Fog::NFV::OpenStack::NotFound
18
+ nil
19
+ end
20
+
21
+ def destroy(uuid)
22
+ vnfd = get(uuid)
23
+ vnfd.destroy
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end