berta 1.0.0
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 +7 -0
- data/.rubocop.yml +26 -0
- data/.travis.yml +19 -0
- data/Gemfile +3 -0
- data/LICENSE +14 -0
- data/README.md +48 -0
- data/Rakefile +17 -0
- data/berta.gemspec +32 -0
- data/bin/berta +4 -0
- data/config/berta.yml +19 -0
- data/config/email.erb +18 -0
- data/lib/berta.rb +13 -0
- data/lib/berta/cli.rb +108 -0
- data/lib/berta/command_executor.rb +18 -0
- data/lib/berta/entities.rb +6 -0
- data/lib/berta/entities/expiration.rb +82 -0
- data/lib/berta/errors.rb +9 -0
- data/lib/berta/errors/backend_error.rb +5 -0
- data/lib/berta/errors/entities.rb +9 -0
- data/lib/berta/errors/entities/invalid_entity_xml_error.rb +7 -0
- data/lib/berta/errors/entities/no_user_email_error.rb +7 -0
- data/lib/berta/errors/opennebula.rb +13 -0
- data/lib/berta/errors/opennebula/authentication_error.rb +7 -0
- data/lib/berta/errors/opennebula/resource_not_found_error.rb +7 -0
- data/lib/berta/errors/opennebula/resource_retrieval_error.rb +7 -0
- data/lib/berta/errors/opennebula/resource_state_error.rb +7 -0
- data/lib/berta/errors/opennebula/stub_error.rb +7 -0
- data/lib/berta/errors/opennebula/user_not_authorized_error.rb +7 -0
- data/lib/berta/errors/standard_error.rb +5 -0
- data/lib/berta/expiration_manager.rb +42 -0
- data/lib/berta/notification_manager.rb +100 -0
- data/lib/berta/service.rb +118 -0
- data/lib/berta/settings.rb +37 -0
- data/lib/berta/utils.rb +6 -0
- data/lib/berta/utils/opennebula.rb +8 -0
- data/lib/berta/utils/opennebula/helper.rb +47 -0
- data/lib/berta/virtual_machine_handler.rb +120 -0
- metadata +304 -0
@@ -0,0 +1,47 @@
|
|
1
|
+
require 'opennebula'
|
2
|
+
|
3
|
+
module Berta
|
4
|
+
module Utils
|
5
|
+
module OpenNebula
|
6
|
+
# Class designed to help with working with OpenNebula
|
7
|
+
class Helper
|
8
|
+
class << self
|
9
|
+
# Hash of errors that can be returned by opennebula
|
10
|
+
ERRORS = Hash.new(Berta::Errors::OpenNebula::ResourceRetrievalError)
|
11
|
+
.update(::OpenNebula::Error::EAUTHENTICATION => Berta::Errors::OpenNebula::AuthenticationError,
|
12
|
+
::OpenNebula::Error::EAUTHORIZATION => Berta::Errors::OpenNebula::UserNotAuthorizedError,
|
13
|
+
::OpenNebula::Error::ENO_EXISTS => Berta::Errors::OpenNebula::ResourceNotFoundError,
|
14
|
+
::OpenNebula::Error::EACTION => Berta::Errors::OpenNebula::ResourceStateError)
|
15
|
+
.freeze
|
16
|
+
# Handles OpenNebula error codes and turns them into exceptions
|
17
|
+
#
|
18
|
+
# @raise [Berta::Errors::OpenNebula::AuthenticationError]
|
19
|
+
# @raise [Berta::Errors::OpenNebula::UserNotAuthorizedError]
|
20
|
+
# @raise [Berta::Errors::OpenNebula::ResourceNotFoundError]
|
21
|
+
# @raise [Berta::Errors::OpenNebula::ResourceStateError]
|
22
|
+
# @raise [Berta::Errors::OpenNebula::ResourceRetrievalError]
|
23
|
+
def handle_error
|
24
|
+
unless block_given?
|
25
|
+
raise Berta::Errors::OpenNebula::StubError, 'OpenNebula service-wrapper was called without a block!'
|
26
|
+
end
|
27
|
+
return_value = yield
|
28
|
+
return return_value unless ::OpenNebula.is_error?(return_value)
|
29
|
+
raise decode_error(return_value.errno), return_value.message
|
30
|
+
end
|
31
|
+
|
32
|
+
# Decodes OpenNebula error codes into exceptions and returns them
|
33
|
+
#
|
34
|
+
# @param errno [OpenNebula::Error] Error code to turn into exception
|
35
|
+
# @return [Berta::Errors::OpenNebula::AuthenticationError]
|
36
|
+
# @return [Berta::Errors::OpenNebula::UserNotAuthorizedError]
|
37
|
+
# @return [Berta::Errors::OpenNebula::ResourceNotFoundError]
|
38
|
+
# @return [Berta::Errors::OpenNebula::ResourceStateError]
|
39
|
+
# @return [Berta::Errors::OpenNebula::ResourceRetrievalError]
|
40
|
+
def decode_error(errno)
|
41
|
+
ERRORS[errno]
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,120 @@
|
|
1
|
+
module Berta
|
2
|
+
# Class for Berta operations on virtual machines
|
3
|
+
class VirtualMachineHandler
|
4
|
+
attr_reader :handle
|
5
|
+
|
6
|
+
# Constructs Virtual machine handler from given vm.
|
7
|
+
#
|
8
|
+
# @param vm [Berta::VirtualMachineHandler] VM that will
|
9
|
+
# this handler use.
|
10
|
+
def initialize(vm)
|
11
|
+
@handle = vm
|
12
|
+
end
|
13
|
+
|
14
|
+
# Sets NOTIFIED value in USER_TEMPLATE to current time
|
15
|
+
# as integer. After updating notified value
|
16
|
+
# fetches data from opennebula database.
|
17
|
+
#
|
18
|
+
# @note This method modifies OpenNebula database
|
19
|
+
# @raise [Berta::Errors::OpenNebula::AuthenticationError]
|
20
|
+
# @raise [Berta::Errors::OpenNebula::UserNotAuthorizedError]
|
21
|
+
# @raise [Berta::Errors::OpenNebula::ResourceNotFoundError]
|
22
|
+
# @raise [Berta::Errors::OpenNebula::ResourceStateError]
|
23
|
+
# @raise [Berta::Errors::OpenNebula::ResourceRetrievalError]
|
24
|
+
def update_notified
|
25
|
+
logger.debug "Setting notified flag of #{handle['ID']} to #{Time.now.to_i}"
|
26
|
+
return if Berta::Settings['dry-run']
|
27
|
+
Berta::Utils::OpenNebula::Helper.handle_error do
|
28
|
+
handle.update("NOTIFIED = #{Time.now.to_i}", true)
|
29
|
+
handle.info
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
# Return NOTIFIED value from USER_TEMPLATE if it is set
|
34
|
+
# else nil.
|
35
|
+
#
|
36
|
+
# @return [Numeric] Time when notified was set else nil.
|
37
|
+
# Time is in UNIX epoch time format.
|
38
|
+
def notified
|
39
|
+
time = handle['USER_TEMPLATE/NOTIFIED']
|
40
|
+
time.to_i if time
|
41
|
+
end
|
42
|
+
|
43
|
+
# Determines if VM meets criteria to be notified.
|
44
|
+
# To be notified, VM musn't be notified and
|
45
|
+
# must have expiration with valid expiration
|
46
|
+
# action in notification interval.
|
47
|
+
#
|
48
|
+
# @return [Boolean] If this vm should be notified.
|
49
|
+
# True if vm should be notified else false.
|
50
|
+
def should_notify?
|
51
|
+
return false if notified
|
52
|
+
expiration = default_expiration
|
53
|
+
return false unless expiration
|
54
|
+
expiration.in_notification_interval?
|
55
|
+
end
|
56
|
+
|
57
|
+
# Adds schelude action to virtual machine. This command
|
58
|
+
# modifies USER_TEMPLATE of virtual machine. But does
|
59
|
+
# not delete old variables is USER_TEMPLATE.
|
60
|
+
#
|
61
|
+
# @note This method modifies OpenNebula database
|
62
|
+
# @param time [Numeric] Time when to notify user
|
63
|
+
# @param action [String] Action to perform on expiration
|
64
|
+
def add_expiration(time, action)
|
65
|
+
logger.debug "Setting expiration date of #{handle['ID']} to #{action} on #{time} with id #{next_sched_action_id}"
|
66
|
+
return if Berta::Settings['dry-run']
|
67
|
+
new_expiration = \
|
68
|
+
Berta::Entities::Expiration.new(next_sched_action_id,
|
69
|
+
time,
|
70
|
+
action)
|
71
|
+
update_expirations(expirations << new_expiration)
|
72
|
+
end
|
73
|
+
|
74
|
+
# Sets array of expirations to vm, rewrites all old ones.
|
75
|
+
# Receiving empty array wont change anything.
|
76
|
+
#
|
77
|
+
# @note This method modifies OpenNebula database
|
78
|
+
# @param exps [Array<Berta::Entities::Expiration>] Expirations to use
|
79
|
+
def update_expirations(exps)
|
80
|
+
template = ''
|
81
|
+
exps.each { |exp| template += exp.template }
|
82
|
+
logger.debug "Setting multiple expirations:\n#{template}"
|
83
|
+
return if Berta::Settings['dry-run']
|
84
|
+
Berta::Utils::OpenNebula::Helper.handle_error do
|
85
|
+
handle.update(template, true)
|
86
|
+
handle.info
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
# Returns array of expirations on vm. Expirations are
|
91
|
+
# classes from USER_TEMPLATE/SCHED_ACTION.
|
92
|
+
#
|
93
|
+
# @return [Array<Berta::Entities::Expiration>] All expirations on vm
|
94
|
+
def expirations
|
95
|
+
exps = []
|
96
|
+
handle.each('USER_TEMPLATE/SCHED_ACTION') \
|
97
|
+
{ |saxml| exps.push(Berta::Entities::Expiration.from_xml(saxml)) }
|
98
|
+
exps
|
99
|
+
end
|
100
|
+
|
101
|
+
# Return default expiration, that means expiration with
|
102
|
+
# default expiration action that is in expiration offset interval
|
103
|
+
# and is closes to current date.
|
104
|
+
#
|
105
|
+
# @return [Berta::Entities::Expiration] Nearest default expiration else nil
|
106
|
+
def default_expiration
|
107
|
+
expirations
|
108
|
+
.find_all { |exp| exp.default_action? && exp.in_expiration_interval? }
|
109
|
+
.min { |exp| exp.time.to_i }
|
110
|
+
end
|
111
|
+
|
112
|
+
private
|
113
|
+
|
114
|
+
def next_sched_action_id
|
115
|
+
elems = handle.retrieve_elements('USER_TEMPLATE/SCHED_ACTION/ID')
|
116
|
+
return 0 unless elems
|
117
|
+
elems.to_a.max.to_i + 1
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
metadata
ADDED
@@ -0,0 +1,304 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: berta
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Dusan Baran
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2017-02-20 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: bundler
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.13'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.13'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rake
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '11.2'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '11.2'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rspec
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '3.5'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '3.5'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: rubocop
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0.42'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0.42'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: rubocop-rspec
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - "~>"
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '1.7'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - "~>"
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '1.7'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: pry
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - "~>"
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0.10'
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - "~>"
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '0.10'
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: webmock
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - "~>"
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '2.3'
|
104
|
+
type: :development
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - "~>"
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '2.3'
|
111
|
+
- !ruby/object:Gem::Dependency
|
112
|
+
name: vcr
|
113
|
+
requirement: !ruby/object:Gem::Requirement
|
114
|
+
requirements:
|
115
|
+
- - "~>"
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: '3.0'
|
118
|
+
type: :development
|
119
|
+
prerelease: false
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
requirements:
|
122
|
+
- - "~>"
|
123
|
+
- !ruby/object:Gem::Version
|
124
|
+
version: '3.0'
|
125
|
+
- !ruby/object:Gem::Dependency
|
126
|
+
name: coveralls
|
127
|
+
requirement: !ruby/object:Gem::Requirement
|
128
|
+
requirements:
|
129
|
+
- - "~>"
|
130
|
+
- !ruby/object:Gem::Version
|
131
|
+
version: '0.8'
|
132
|
+
type: :development
|
133
|
+
prerelease: false
|
134
|
+
version_requirements: !ruby/object:Gem::Requirement
|
135
|
+
requirements:
|
136
|
+
- - "~>"
|
137
|
+
- !ruby/object:Gem::Version
|
138
|
+
version: '0.8'
|
139
|
+
- !ruby/object:Gem::Dependency
|
140
|
+
name: thor
|
141
|
+
requirement: !ruby/object:Gem::Requirement
|
142
|
+
requirements:
|
143
|
+
- - "~>"
|
144
|
+
- !ruby/object:Gem::Version
|
145
|
+
version: '0.19'
|
146
|
+
type: :runtime
|
147
|
+
prerelease: false
|
148
|
+
version_requirements: !ruby/object:Gem::Requirement
|
149
|
+
requirements:
|
150
|
+
- - "~>"
|
151
|
+
- !ruby/object:Gem::Version
|
152
|
+
version: '0.19'
|
153
|
+
- !ruby/object:Gem::Dependency
|
154
|
+
name: settingslogic
|
155
|
+
requirement: !ruby/object:Gem::Requirement
|
156
|
+
requirements:
|
157
|
+
- - "~>"
|
158
|
+
- !ruby/object:Gem::Version
|
159
|
+
version: '2.0'
|
160
|
+
type: :runtime
|
161
|
+
prerelease: false
|
162
|
+
version_requirements: !ruby/object:Gem::Requirement
|
163
|
+
requirements:
|
164
|
+
- - "~>"
|
165
|
+
- !ruby/object:Gem::Version
|
166
|
+
version: '2.0'
|
167
|
+
- !ruby/object:Gem::Dependency
|
168
|
+
name: yell
|
169
|
+
requirement: !ruby/object:Gem::Requirement
|
170
|
+
requirements:
|
171
|
+
- - "~>"
|
172
|
+
- !ruby/object:Gem::Version
|
173
|
+
version: '2.0'
|
174
|
+
type: :runtime
|
175
|
+
prerelease: false
|
176
|
+
version_requirements: !ruby/object:Gem::Requirement
|
177
|
+
requirements:
|
178
|
+
- - "~>"
|
179
|
+
- !ruby/object:Gem::Version
|
180
|
+
version: '2.0'
|
181
|
+
- !ruby/object:Gem::Dependency
|
182
|
+
name: opennebula
|
183
|
+
requirement: !ruby/object:Gem::Requirement
|
184
|
+
requirements:
|
185
|
+
- - "~>"
|
186
|
+
- !ruby/object:Gem::Version
|
187
|
+
version: '5.2'
|
188
|
+
type: :runtime
|
189
|
+
prerelease: false
|
190
|
+
version_requirements: !ruby/object:Gem::Requirement
|
191
|
+
requirements:
|
192
|
+
- - "~>"
|
193
|
+
- !ruby/object:Gem::Version
|
194
|
+
version: '5.2'
|
195
|
+
- !ruby/object:Gem::Dependency
|
196
|
+
name: chronic_duration
|
197
|
+
requirement: !ruby/object:Gem::Requirement
|
198
|
+
requirements:
|
199
|
+
- - "~>"
|
200
|
+
- !ruby/object:Gem::Version
|
201
|
+
version: '0.10'
|
202
|
+
type: :runtime
|
203
|
+
prerelease: false
|
204
|
+
version_requirements: !ruby/object:Gem::Requirement
|
205
|
+
requirements:
|
206
|
+
- - "~>"
|
207
|
+
- !ruby/object:Gem::Version
|
208
|
+
version: '0.10'
|
209
|
+
- !ruby/object:Gem::Dependency
|
210
|
+
name: mail
|
211
|
+
requirement: !ruby/object:Gem::Requirement
|
212
|
+
requirements:
|
213
|
+
- - "~>"
|
214
|
+
- !ruby/object:Gem::Version
|
215
|
+
version: '2.6'
|
216
|
+
type: :runtime
|
217
|
+
prerelease: false
|
218
|
+
version_requirements: !ruby/object:Gem::Requirement
|
219
|
+
requirements:
|
220
|
+
- - "~>"
|
221
|
+
- !ruby/object:Gem::Version
|
222
|
+
version: '2.6'
|
223
|
+
- !ruby/object:Gem::Dependency
|
224
|
+
name: tilt
|
225
|
+
requirement: !ruby/object:Gem::Requirement
|
226
|
+
requirements:
|
227
|
+
- - "~>"
|
228
|
+
- !ruby/object:Gem::Version
|
229
|
+
version: '2.0'
|
230
|
+
type: :runtime
|
231
|
+
prerelease: false
|
232
|
+
version_requirements: !ruby/object:Gem::Requirement
|
233
|
+
requirements:
|
234
|
+
- - "~>"
|
235
|
+
- !ruby/object:Gem::Version
|
236
|
+
version: '2.0'
|
237
|
+
description: Berta will check all VMs on OpenNebula cloud for expiration date
|
238
|
+
email: dbaran@hotmail.sk
|
239
|
+
executables:
|
240
|
+
- berta
|
241
|
+
extensions: []
|
242
|
+
extra_rdoc_files: []
|
243
|
+
files:
|
244
|
+
- ".rubocop.yml"
|
245
|
+
- ".travis.yml"
|
246
|
+
- Gemfile
|
247
|
+
- LICENSE
|
248
|
+
- README.md
|
249
|
+
- Rakefile
|
250
|
+
- berta.gemspec
|
251
|
+
- bin/berta
|
252
|
+
- config/berta.yml
|
253
|
+
- config/email.erb
|
254
|
+
- lib/berta.rb
|
255
|
+
- lib/berta/cli.rb
|
256
|
+
- lib/berta/command_executor.rb
|
257
|
+
- lib/berta/entities.rb
|
258
|
+
- lib/berta/entities/expiration.rb
|
259
|
+
- lib/berta/errors.rb
|
260
|
+
- lib/berta/errors/backend_error.rb
|
261
|
+
- lib/berta/errors/entities.rb
|
262
|
+
- lib/berta/errors/entities/invalid_entity_xml_error.rb
|
263
|
+
- lib/berta/errors/entities/no_user_email_error.rb
|
264
|
+
- lib/berta/errors/opennebula.rb
|
265
|
+
- lib/berta/errors/opennebula/authentication_error.rb
|
266
|
+
- lib/berta/errors/opennebula/resource_not_found_error.rb
|
267
|
+
- lib/berta/errors/opennebula/resource_retrieval_error.rb
|
268
|
+
- lib/berta/errors/opennebula/resource_state_error.rb
|
269
|
+
- lib/berta/errors/opennebula/stub_error.rb
|
270
|
+
- lib/berta/errors/opennebula/user_not_authorized_error.rb
|
271
|
+
- lib/berta/errors/standard_error.rb
|
272
|
+
- lib/berta/expiration_manager.rb
|
273
|
+
- lib/berta/notification_manager.rb
|
274
|
+
- lib/berta/service.rb
|
275
|
+
- lib/berta/settings.rb
|
276
|
+
- lib/berta/utils.rb
|
277
|
+
- lib/berta/utils/opennebula.rb
|
278
|
+
- lib/berta/utils/opennebula/helper.rb
|
279
|
+
- lib/berta/virtual_machine_handler.rb
|
280
|
+
homepage: https://github.com/dudoslav/berta
|
281
|
+
licenses:
|
282
|
+
- Apache License, Version 2.0
|
283
|
+
metadata: {}
|
284
|
+
post_install_message:
|
285
|
+
rdoc_options: []
|
286
|
+
require_paths:
|
287
|
+
- lib
|
288
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
289
|
+
requirements:
|
290
|
+
- - ">="
|
291
|
+
- !ruby/object:Gem::Version
|
292
|
+
version: '0'
|
293
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
294
|
+
requirements:
|
295
|
+
- - ">="
|
296
|
+
- !ruby/object:Gem::Version
|
297
|
+
version: '0'
|
298
|
+
requirements: []
|
299
|
+
rubyforge_project:
|
300
|
+
rubygems_version: 2.6.10
|
301
|
+
signing_key:
|
302
|
+
specification_version: 4
|
303
|
+
summary: Berta VM expiration tool
|
304
|
+
test_files: []
|