nexpose 0.6.5 → 0.7.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 +4 -4
- data/COPYING +33 -0
- data/README.markdown +6 -2
- data/lib/nexpose.rb +6 -0
- data/lib/nexpose/ajax.rb +27 -6
- data/lib/nexpose/dag.rb +5 -13
- data/lib/nexpose/data_table.rb +1 -1
- data/lib/nexpose/discovery.rb +191 -0
- data/lib/nexpose/discovery/filter.rb +36 -0
- data/lib/nexpose/filter.rb +17 -6
- data/lib/nexpose/group.rb +21 -8
- data/lib/nexpose/multi_tenant_user.rb +219 -0
- data/lib/nexpose/report_template.rb +1 -1
- data/lib/nexpose/scan.rb +11 -0
- data/lib/nexpose/silo.rb +282 -278
- data/lib/nexpose/silo_profile.rb +239 -0
- data/lib/nexpose/site.rb +112 -5
- data/lib/nexpose/tag.rb +343 -0
- data/lib/nexpose/tag/criteria.rb +45 -0
- metadata +9 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 28266093c49d6743e5a662298252c67779d54659
|
4
|
+
data.tar.gz: e1525369d7ee0b2052c87c0b3d14f763085f5276
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: baa5a71852ad9259b8086ad9e0a6983cedd152f6da5efbbd3278070e4b633c4c81abd1eeb414aacb33a9010705df40ba30a46e30d85e103028a796f0c3883e10
|
7
|
+
data.tar.gz: 481d97ce05e9b5976a3f679dcc502d6f071db593e28dab6fd4be00ccfc365680405c2eda62cb5bdb897059e1de1e78e830043e05b52089cf8303efc684b51c79
|
data/COPYING
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
Copyright (C) 2014, Rapid7, Inc.
|
2
|
+
All rights reserved.
|
3
|
+
|
4
|
+
Redistribution and use in source and binary forms, with or without modification,
|
5
|
+
are permitted provided that the following conditions are met:
|
6
|
+
|
7
|
+
* Redistributions of source code must retain the above copyright notice,
|
8
|
+
this list of conditions and the following disclaimer.
|
9
|
+
|
10
|
+
* Redistributions in binary form must reproduce the above copyright notice,
|
11
|
+
this list of conditions and the following disclaimer in the documentation
|
12
|
+
and/or other materials provided with the distribution.
|
13
|
+
|
14
|
+
* Neither the name of Rapid7, Inc. nor the names of its contributors
|
15
|
+
may be used to endorse or promote products derived from this software
|
16
|
+
without specific prior written permission.
|
17
|
+
|
18
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
19
|
+
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
20
|
+
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
21
|
+
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
22
|
+
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
23
|
+
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
24
|
+
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
25
|
+
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
26
|
+
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
27
|
+
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
28
|
+
|
29
|
+
================================================================================
|
30
|
+
|
31
|
+
The nexpose-client gem is provided under the 3-clause BSD license above.
|
32
|
+
|
33
|
+
The copyright on this package is held by Rapid7, Inc.
|
data/README.markdown
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
# Nexpose
|
1
|
+
# Nexpose-Client
|
2
2
|
|
3
3
|
This is the official gem package for the Ruby Nexpose API.
|
4
4
|
|
@@ -19,6 +19,10 @@ Our coding standards include:
|
|
19
19
|
* Unless otherwise noted, code should adhere to the Ruby Style Guide: https://github.com/bbatsov/ruby-style-guide
|
20
20
|
* Use YARDoc comment style to improve the API documentation of the gem.
|
21
21
|
|
22
|
+
## License
|
23
|
+
|
24
|
+
The nexpose-client gem is provided under the 3-Clause BSD License. See [COPYING](COPYING) for details.
|
25
|
+
|
22
26
|
## Credits
|
23
27
|
|
24
|
-
Rapid7
|
28
|
+
Rapid7, Inc.
|
data/lib/nexpose.rb
CHANGED
@@ -66,11 +66,14 @@ require 'nexpose/creds'
|
|
66
66
|
require 'nexpose/shared_cred'
|
67
67
|
require 'nexpose/data_table'
|
68
68
|
require 'nexpose/device'
|
69
|
+
require 'nexpose/discovery'
|
70
|
+
require 'nexpose/discovery/filter'
|
69
71
|
require 'nexpose/engine'
|
70
72
|
require 'nexpose/filter'
|
71
73
|
require 'nexpose/group'
|
72
74
|
require 'nexpose/dag'
|
73
75
|
require 'nexpose/manage'
|
76
|
+
require 'nexpose/multi_tenant_user'
|
74
77
|
require 'nexpose/pool'
|
75
78
|
require 'nexpose/report'
|
76
79
|
require 'nexpose/report_template'
|
@@ -78,7 +81,10 @@ require 'nexpose/role'
|
|
78
81
|
require 'nexpose/scan'
|
79
82
|
require 'nexpose/scan_template'
|
80
83
|
require 'nexpose/silo'
|
84
|
+
require 'nexpose/silo_profile'
|
81
85
|
require 'nexpose/site'
|
86
|
+
require 'nexpose/tag'
|
87
|
+
require 'nexpose/tag/criteria'
|
82
88
|
require 'nexpose/ticket'
|
83
89
|
require 'nexpose/user'
|
84
90
|
require 'nexpose/vuln'
|
data/lib/nexpose/ajax.rb
CHANGED
@@ -10,6 +10,12 @@ module Nexpose
|
|
10
10
|
module AJAX
|
11
11
|
module_function
|
12
12
|
|
13
|
+
module CONTENT_TYPE
|
14
|
+
XML = 'text/xml; charset=UTF-8'
|
15
|
+
JSON = 'application/json; charset-utf-8'
|
16
|
+
FORM = 'application/x-www-form-urlencoded; charset=UTF-8'
|
17
|
+
end
|
18
|
+
|
13
19
|
# GET call to a Nexpose controller.
|
14
20
|
#
|
15
21
|
# @param [Connection] nsc API connection to a Nexpose console.
|
@@ -17,7 +23,7 @@ module Nexpose
|
|
17
23
|
# @param [String] content_type Content type to use when issuing the GET.
|
18
24
|
# @return [String|REXML::Document|Hash] The response from the call.
|
19
25
|
#
|
20
|
-
def get(nsc, uri, content_type =
|
26
|
+
def get(nsc, uri, content_type = CONTENT_TYPE::XML)
|
21
27
|
get = Net::HTTP::Get.new(uri)
|
22
28
|
get.set_content_type(content_type)
|
23
29
|
_request(nsc, get)
|
@@ -31,7 +37,7 @@ module Nexpose
|
|
31
37
|
# @param [String] content_type Content type to use when issuing the PUT.
|
32
38
|
# @return [String] The response from the call.
|
33
39
|
#
|
34
|
-
def put(nsc, uri, payload = nil, content_type =
|
40
|
+
def put(nsc, uri, payload = nil, content_type = CONTENT_TYPE::XML)
|
35
41
|
put = Net::HTTP::Put.new(uri)
|
36
42
|
put.set_content_type(content_type)
|
37
43
|
put.body = payload.to_s if payload
|
@@ -46,13 +52,28 @@ module Nexpose
|
|
46
52
|
# @param [String] content_type Content type to use when issuing the POST.
|
47
53
|
# @return [String|REXML::Document|Hash] The response from the call.
|
48
54
|
#
|
49
|
-
def post(nsc, uri, payload = nil, content_type =
|
55
|
+
def post(nsc, uri, payload = nil, content_type = CONTENT_TYPE::XML)
|
50
56
|
post = Net::HTTP::Post.new(uri)
|
51
57
|
post.set_content_type(content_type)
|
52
58
|
post.body = payload.to_s if payload
|
53
59
|
_request(nsc, post)
|
54
60
|
end
|
55
61
|
|
62
|
+
# PATCH call to a Nexpose controller.
|
63
|
+
#
|
64
|
+
# @param [Connection] nsc API connection to a Nexpose console.
|
65
|
+
# @param [String] uri Controller address relative to https://host:port
|
66
|
+
# @param [String|REXML::Document] payload XML document required by the call.
|
67
|
+
# @param [String] content_type Content type to use when issuing the PATCH.
|
68
|
+
# @return [String] The response from the call.
|
69
|
+
#
|
70
|
+
def patch(nsc, uri, payload = nil, content_type = CONTENT_TYPE::XML)
|
71
|
+
patch = Net::HTTP::Patch.new(uri)
|
72
|
+
patch.set_content_type(content_type)
|
73
|
+
patch.body = payload.to_s if payload
|
74
|
+
_request(nsc, patch)
|
75
|
+
end
|
76
|
+
|
56
77
|
# POST call to a Nexpose controller that uses a form-post model.
|
57
78
|
# This is here to support legacy use of POST in old controllers.
|
58
79
|
#
|
@@ -63,7 +84,7 @@ module Nexpose
|
|
63
84
|
# @param [String] content_type Content type to use when issuing the POST.
|
64
85
|
# @return [Hash] The parsed JSON response from the call.
|
65
86
|
#
|
66
|
-
def form_post(nsc, uri, parameters, content_type =
|
87
|
+
def form_post(nsc, uri, parameters, content_type = CONTENT_TYPE::FORM)
|
67
88
|
post = Net::HTTP::Post.new(uri)
|
68
89
|
post.set_content_type(content_type)
|
69
90
|
post.set_form_data(parameters)
|
@@ -75,7 +96,7 @@ module Nexpose
|
|
75
96
|
# @param [Connection] nsc API connection to a Nexpose console.
|
76
97
|
# @param [String] uri Controller address relative to https://host:port
|
77
98
|
# @param [String] content_type Content type to use when issuing the DELETE.
|
78
|
-
def delete(nsc, uri, content_type =
|
99
|
+
def delete(nsc, uri, content_type = CONTENT_TYPE::XML)
|
79
100
|
delete = Net::HTTP::Delete.new(uri)
|
80
101
|
delete.set_content_type(content_type)
|
81
102
|
_request(nsc, delete)
|
@@ -89,7 +110,7 @@ module Nexpose
|
|
89
110
|
# @return [Hash] The parametrized URI.
|
90
111
|
|
91
112
|
def parametrize_uri(uri, parameters)
|
92
|
-
uri = uri.concat(('?').concat(parameters.map { |k, v| "#{k}=#{CGI.escape(v
|
113
|
+
uri = uri.concat(('?').concat(parameters.map { |k, v| "#{k}=#{CGI.escape(v.to_s)}" }.join('&'))) if parameters
|
93
114
|
end
|
94
115
|
|
95
116
|
###
|
data/lib/nexpose/dag.rb
CHANGED
@@ -31,9 +31,9 @@ module Nexpose
|
|
31
31
|
# load includes admin users, but save will fail if they are included.
|
32
32
|
admins = nsc.users.select { |u| u.is_admin }.map { |u| u.id }
|
33
33
|
@users.reject! { |id| admins.member? id }
|
34
|
-
|
35
|
-
|
36
|
-
|
34
|
+
params = @id ? { 'entityid' => @id, 'mode' => 'edit' } : { 'entityid' => false, 'mode' => false }
|
35
|
+
uri = AJAX.parametrize_uri('/data/assetGroup/saveAssetGroup', params)
|
36
|
+
data = JSON.parse(AJAX.post(nsc, uri, _to_entity_details, AJAX::CONTENT_TYPE::JSON))
|
37
37
|
data['response'] == 'success.'
|
38
38
|
end
|
39
39
|
|
@@ -53,21 +53,13 @@ module Nexpose
|
|
53
53
|
dag
|
54
54
|
end
|
55
55
|
|
56
|
-
def
|
56
|
+
def _to_entity_details
|
57
57
|
obj = { 'searchCriteria' => @criteria.to_map,
|
58
58
|
'name' => @name,
|
59
59
|
'tag' => @description.nil? ? '' : @description,
|
60
60
|
'dynamic' => true,
|
61
61
|
'users' => @users }
|
62
|
-
|
63
|
-
if @id
|
64
|
-
map['entityid'] = @id
|
65
|
-
map['mode'] = 'edit'
|
66
|
-
else
|
67
|
-
map['entityid'] = false
|
68
|
-
map['mode'] = false
|
69
|
-
end
|
70
|
-
map
|
62
|
+
JSON.generate(obj)
|
71
63
|
end
|
72
64
|
end
|
73
65
|
end
|
data/lib/nexpose/data_table.rb
CHANGED
@@ -28,7 +28,7 @@ module Nexpose
|
|
28
28
|
# 'table-id' => 'site-assets',
|
29
29
|
# 'siteID' => site_id })
|
30
30
|
#
|
31
|
-
def _get_json_table(console, address, parameters, page_size = 500, records = nil)
|
31
|
+
def _get_json_table(console, address, parameters = {}, page_size = 500, records = nil)
|
32
32
|
parameters['dir'] = 'DESC'
|
33
33
|
parameters['startIndex'] = -1
|
34
34
|
parameters['results'] = -1
|
@@ -0,0 +1,191 @@
|
|
1
|
+
module Nexpose
|
2
|
+
|
3
|
+
class Connection
|
4
|
+
|
5
|
+
# Retrieve information about all available connections for dynamic
|
6
|
+
# discovery of assets, including whether or not connections are active.
|
7
|
+
#
|
8
|
+
def list_discovery_connections
|
9
|
+
xml = make_xml('DiscoveryConnectionListingRequest')
|
10
|
+
response = execute(xml, '1.2')
|
11
|
+
connections = []
|
12
|
+
response.res.elements.each('DiscoveryConnectionListingResponse/DiscoveryConnectionSummary') do |conn|
|
13
|
+
connections << DiscoveryConnection.parse(conn)
|
14
|
+
end
|
15
|
+
connections
|
16
|
+
end
|
17
|
+
alias_method :discovery_connections, :list_discovery_connections
|
18
|
+
|
19
|
+
# Delete an existing connection to a target used for dynamic discovery of assets.
|
20
|
+
#
|
21
|
+
# @param [Fixnum] id ID of an existing discovery connection.
|
22
|
+
#
|
23
|
+
def delete_discovery_connection(id)
|
24
|
+
xml = make_xml('DiscoveryConnectionDeleteRequest', { 'id' => id })
|
25
|
+
response = execute(xml, '1.2')
|
26
|
+
response.success
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
class DiscoveryConnection
|
31
|
+
include XMLUtils
|
32
|
+
|
33
|
+
module Protocol
|
34
|
+
HTTP = 'HTTP'
|
35
|
+
HTTPS = 'HTTPS'
|
36
|
+
end
|
37
|
+
|
38
|
+
# A unique identifier for this connection.
|
39
|
+
attr_accessor :id
|
40
|
+
|
41
|
+
# A unique name for this connection.
|
42
|
+
attr_accessor :name
|
43
|
+
|
44
|
+
# The IP address or fully qualified domain name of the server.
|
45
|
+
attr_accessor :address
|
46
|
+
|
47
|
+
# A user name that can be used to log into the server.
|
48
|
+
attr_accessor :user
|
49
|
+
|
50
|
+
# The password to use when connecting with the defined user.
|
51
|
+
attr_accessor :password
|
52
|
+
|
53
|
+
# The protocol used for conneting to the server. One of DiscoveryConnection::Protocol
|
54
|
+
attr_accessor :protocol
|
55
|
+
|
56
|
+
# The port used for connecting to the server. A valid port from 1 to 65535.
|
57
|
+
attr_accessor :port
|
58
|
+
|
59
|
+
# Whether or not the connection is active.
|
60
|
+
# Discovery is only possible when the connection is active.
|
61
|
+
attr_accessor :status
|
62
|
+
|
63
|
+
# Create a new discovery connection.
|
64
|
+
#
|
65
|
+
# @param [String] name Name to assign to this connection.
|
66
|
+
# @param [String] address IP or fully qualified domain name of the
|
67
|
+
# connection server.
|
68
|
+
# @param [String] user User name for credentials on this connection.
|
69
|
+
# @param [String] password Password for credentials on this connection.
|
70
|
+
#
|
71
|
+
def initialize(name, address, user, password = nil)
|
72
|
+
@name, @address, @user, @password = name, address, user, password
|
73
|
+
@id = -1
|
74
|
+
@port = 443
|
75
|
+
@protocol = Protocol::HTTPS
|
76
|
+
end
|
77
|
+
|
78
|
+
# Save this discovery connection to a Nexpose console.
|
79
|
+
#
|
80
|
+
# @param [Connection] nsc Connection to a console.
|
81
|
+
#
|
82
|
+
def save(nsc)
|
83
|
+
if @id == -1
|
84
|
+
xml = nsc.make_xml('DiscoveryConnectionCreateRequest')
|
85
|
+
else
|
86
|
+
xml = nsc.make_xml('DiscoveryConnectionUpdateRequest')
|
87
|
+
end
|
88
|
+
xml.add_element(as_xml)
|
89
|
+
response = nsc.execute(xml, '1.2')
|
90
|
+
if response.success
|
91
|
+
ret = REXML::XPath.first(response.res, 'DiscoveryConnectionCreateResponse')
|
92
|
+
@id = ret.attributes['id'].to_i unless ret.nil?
|
93
|
+
end
|
94
|
+
@id
|
95
|
+
end
|
96
|
+
|
97
|
+
# Perform dynamic discover of assets against this connection.
|
98
|
+
#
|
99
|
+
# @param [Connection] nsc Connection to a console.
|
100
|
+
# @param [Criteria] criteria Criteria search object narrowing which assets
|
101
|
+
# to filter.
|
102
|
+
# @return [Array[DiscoveredAsset]] All discovered assets matching the criteria.
|
103
|
+
#
|
104
|
+
def discover(nsc, criteria = nil)
|
105
|
+
parameters = { 'table-id' => 'assetdiscovery',
|
106
|
+
'sort' => 'assetDiscoveryName',
|
107
|
+
'searchCriteria' => criteria.nil? ? 'null' : criteria.to_json,
|
108
|
+
'configID' => @id }
|
109
|
+
data = DataTable._get_json_table(nsc, '/data/discoveryAsset/discoverAssets', parameters)
|
110
|
+
data.map { |a| DiscoveredAsset.parse(a) }
|
111
|
+
end
|
112
|
+
|
113
|
+
# Initiates a connection to a target used for dynamic discovery of assets.
|
114
|
+
# As long as a connection is active, dynamic discovery is continuous.
|
115
|
+
#
|
116
|
+
# @param [Connection] nsc Connection to a console.
|
117
|
+
#
|
118
|
+
def connect(nsc)
|
119
|
+
xml = nsc.make_xml('DiscoveryConnectionConnectRequest', { 'id' => id })
|
120
|
+
response = nsc.execute(xml, '1.2')
|
121
|
+
response.success
|
122
|
+
end
|
123
|
+
|
124
|
+
# Delete this connection from the console.
|
125
|
+
#
|
126
|
+
# @param [Connection] nsc Connection to a console.
|
127
|
+
#
|
128
|
+
def delete(nsc)
|
129
|
+
nsc.delete_discovery_connection(@id)
|
130
|
+
end
|
131
|
+
|
132
|
+
def as_xml
|
133
|
+
xml = REXML::Element.new('DiscoveryConnection')
|
134
|
+
xml.add_attributes({ 'name' => @name,
|
135
|
+
'address' => @address,
|
136
|
+
'port' => @port,
|
137
|
+
'protocol' => @protocol,
|
138
|
+
'user-name' => @user,
|
139
|
+
'password' => @password })
|
140
|
+
xml
|
141
|
+
end
|
142
|
+
|
143
|
+
def to_xml
|
144
|
+
as_xml.to_s
|
145
|
+
end
|
146
|
+
|
147
|
+
def self.parse(xml)
|
148
|
+
conn = new(xml.attributes['name'],
|
149
|
+
xml.attributes['address'],
|
150
|
+
xml.attributes['user-name'])
|
151
|
+
conn.id = xml.attributes['id'].to_i
|
152
|
+
conn.protocol = xml.attributes['protocol']
|
153
|
+
conn.port = xml.attributes['port'].to_i
|
154
|
+
conn.status = xml.attributes['connection-status']
|
155
|
+
conn
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
159
|
+
class DiscoveredAsset
|
160
|
+
|
161
|
+
attr_accessor :name
|
162
|
+
attr_accessor :ip
|
163
|
+
attr_accessor :host
|
164
|
+
attr_accessor :datacenter
|
165
|
+
attr_accessor :cluster
|
166
|
+
attr_accessor :pool
|
167
|
+
attr_accessor :os
|
168
|
+
attr_accessor :status
|
169
|
+
|
170
|
+
def initialize(&block)
|
171
|
+
instance_eval &block if block_given?
|
172
|
+
end
|
173
|
+
|
174
|
+
def on?
|
175
|
+
@status == 'On'
|
176
|
+
end
|
177
|
+
|
178
|
+
def self.parse(json)
|
179
|
+
new do |asset|
|
180
|
+
asset.ip = json['IPAddress']
|
181
|
+
asset.os = json['OSName']
|
182
|
+
asset.name = json['assetDiscoveryName']
|
183
|
+
asset.cluster = json['cluster']
|
184
|
+
asset.datacenter = json['datacenter']
|
185
|
+
asset.host = json['host']
|
186
|
+
asset.status = json['powerStatus']
|
187
|
+
asset.pool = json['resourcePool']
|
188
|
+
end
|
189
|
+
end
|
190
|
+
end
|
191
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
module Nexpose
|
2
|
+
module Search
|
3
|
+
module Field
|
4
|
+
|
5
|
+
# Valid Operators: IS, IS_NOT, CONTAINS, NOT_CONTAINS, STARTS_WITH
|
6
|
+
CLUSTER = 'CLUSTER'
|
7
|
+
|
8
|
+
# Valid Operators: IS, IS_NOT
|
9
|
+
DATACENTER ='DATACENTER'
|
10
|
+
|
11
|
+
# Valid Operators: CONTAINS, NOT_CONTAINS
|
12
|
+
GUEST_OS_FAMILY = 'GUEST_OS_FAMILY'
|
13
|
+
|
14
|
+
# Valid Operators: IN, NOT_IN
|
15
|
+
IP_ADDRESS_RANGE = 'IP_ADDRESS'
|
16
|
+
|
17
|
+
# Valid Operators: IN, NOT_IN
|
18
|
+
# Valid Values (See Value::PowerState): ON, OFF, SUSPENDED
|
19
|
+
POWER_STATE = 'POWER_STATE'
|
20
|
+
|
21
|
+
# Valid Operators: CONTAINS, NOT_CONTAINS
|
22
|
+
RESOURCE_POOL_PATH ='RESOURCE_POOL_PATH'
|
23
|
+
|
24
|
+
# Valid Operators: IS, IS_NOT, CONTAINS, NOT_CONTAINS, STARTS_WITH
|
25
|
+
VIRTUAL_MACHINE_NAME = 'VM'
|
26
|
+
end
|
27
|
+
|
28
|
+
module Value
|
29
|
+
module PowerState
|
30
|
+
ON = 'poweredOn'
|
31
|
+
OFF = 'poweredOff'
|
32
|
+
SUSPENDED = 'suspended'
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|