metasploit_data_models 0.7.0-java → 0.11.2-java
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +3 -0
- data/.travis.yml +1 -0
- data/app/models/mdm/host.rb +352 -26
- data/app/models/mdm/loot.rb +72 -7
- data/app/models/mdm/{module_action.rb → module/action.rb} +3 -3
- data/app/models/mdm/{module_arch.rb → module/arch.rb} +3 -3
- data/app/models/mdm/{module_author.rb → module/author.rb} +3 -3
- data/app/models/mdm/module/detail.rb +280 -0
- data/app/models/mdm/{module_mixin.rb → module/mixin.rb} +3 -3
- data/app/models/mdm/{module_platform.rb → module/platform.rb} +3 -3
- data/app/models/mdm/module/ref.rb +48 -0
- data/app/models/mdm/{module_target.rb → module/target.rb} +3 -3
- data/app/models/mdm/note.rb +61 -6
- data/app/models/mdm/ref.rb +39 -1
- data/app/models/mdm/service.rb +85 -7
- data/app/models/mdm/session.rb +100 -6
- data/app/models/mdm/vuln.rb +104 -24
- data/db/migrate/20130228214900_change_required_columns_to_null_false_in_web_vulns.rb +1 -17
- data/db/migrate/20130412154159_change_foreign_key_in_module_actions.rb +25 -0
- data/db/migrate/20130412171844_change_foreign_key_in_module_archs.rb +25 -0
- data/db/migrate/20130412173121_change_foreign_key_in_module_authors.rb +25 -0
- data/db/migrate/20130412173640_change_foreign_key_in_module_mixins.rb +25 -0
- data/db/migrate/20130412174254_change_foreign_key_in_module_platforms.rb +25 -0
- data/db/migrate/20130412174719_change_foreign_key_in_module_refs.rb +25 -0
- data/db/migrate/20130412175040_change_foreign_key_in_module_targets.rb +25 -0
- data/db/migrate/20130430151353_change_required_columns_to_null_false_in_hosts.rb +11 -0
- data/db/migrate/20130430162145_enforce_address_uniqueness_in_workspace_in_hosts.rb +23 -0
- data/lib/mdm/module.rb +4 -0
- data/lib/metasploit_data_models.rb +1 -0
- data/lib/metasploit_data_models/change_required_columns_to_null_false.rb +23 -0
- data/lib/metasploit_data_models/version.rb +1 -1
- data/spec/app/models/mdm/host_spec.rb +411 -0
- data/spec/app/models/mdm/host_tag_spec.rb +13 -0
- data/spec/app/models/mdm/{module_action_spec.rb → module/action_spec.rb} +6 -6
- data/spec/app/models/mdm/{module_arch_spec.rb → module/arch_spec.rb} +6 -6
- data/spec/app/models/mdm/{module_author_spec.rb → module/author_spec.rb} +6 -6
- data/spec/app/models/mdm/{module_detail_spec.rb → module/detail_spec.rb} +101 -11
- data/spec/app/models/mdm/{module_mixin_spec.rb → module/mixin_spec.rb} +6 -6
- data/spec/app/models/mdm/{module_platform_spec.rb → module/platform_spec.rb} +6 -6
- data/spec/app/models/mdm/module/ref_spec.rb +62 -0
- data/spec/app/models/mdm/{module_target_spec.rb → module/target_spec.rb} +6 -6
- data/spec/app/models/mdm/ref_spec.rb +62 -0
- data/spec/app/models/mdm/tag_spec.rb +13 -0
- data/spec/app/models/mdm/vuln_ref_spec.rb +13 -0
- data/spec/app/models/mdm/vuln_spec.rb +231 -0
- data/spec/dummy/db/schema.rb +20 -20
- data/spec/factories/mdm/host_tags.rb +9 -0
- data/spec/factories/mdm/hosts.rb +65 -0
- data/spec/factories/mdm/module/actions.rb +14 -0
- data/spec/factories/mdm/module/archs.rb +14 -0
- data/spec/factories/mdm/{module_authors.rb → module/authors.rb} +4 -4
- data/spec/factories/mdm/module/details.rb +66 -0
- data/spec/factories/mdm/module/mixins.rb +14 -0
- data/spec/factories/mdm/module/platforms.rb +14 -0
- data/spec/factories/mdm/module/refs.rb +14 -0
- data/spec/factories/mdm/{module_targets.rb → module/targets.rb} +3 -3
- data/spec/factories/mdm/refs.rb +9 -0
- data/spec/factories/mdm/tags.rb +14 -0
- data/spec/factories/mdm/vuln_refs.rb +4 -0
- data/spec/factories/mdm/vulns.rb +20 -0
- metadata +75 -42
- data/app/models/mdm/module_detail.rb +0 -59
- data/app/models/mdm/module_ref.rb +0 -24
- data/spec/app/models/mdm/module_ref_spec.rb +0 -38
- data/spec/factories/mdm/module_actions.rb +0 -14
- data/spec/factories/mdm/module_archs.rb +0 -14
- data/spec/factories/mdm/module_details.rb +0 -9
- data/spec/factories/mdm/module_mixins.rb +0 -14
- data/spec/factories/mdm/module_platforms.rb +0 -14
- data/spec/factories/mdm/module_refs.rb +0 -14
data/.gitignore
CHANGED
data/.travis.yml
CHANGED
data/app/models/mdm/host.rb
CHANGED
@@ -1,57 +1,355 @@
|
|
1
|
-
|
1
|
+
# A system with an {#address IP address} on the network that has been discovered in some way.
|
2
2
|
class Mdm::Host < ActiveRecord::Base
|
3
3
|
include Mdm::Host::OperatingSystemNormalization
|
4
4
|
|
5
5
|
#
|
6
|
-
#
|
6
|
+
# CONSTANTS
|
7
7
|
#
|
8
8
|
|
9
|
-
|
9
|
+
# Either the CPU architecture for native code or the programming language name for exploits that run code in the
|
10
|
+
# programming language's virtual machine.
|
11
|
+
ARCHITECTURES = [
|
12
|
+
'armbe',
|
13
|
+
'armle',
|
14
|
+
'cbea',
|
15
|
+
'cbea64',
|
16
|
+
'cmd',
|
17
|
+
'java',
|
18
|
+
'mips',
|
19
|
+
'mipsbe',
|
20
|
+
'mipsle',
|
21
|
+
'php',
|
22
|
+
'ppc',
|
23
|
+
'ppc64',
|
24
|
+
'ruby',
|
25
|
+
'sparc',
|
26
|
+
'tty',
|
27
|
+
# To be used for compatability with 'X86_64'
|
28
|
+
'x64',
|
29
|
+
'x86',
|
30
|
+
'x86_64'
|
31
|
+
]
|
10
32
|
|
11
|
-
#
|
12
|
-
|
13
|
-
|
33
|
+
# Valid values for {#os_name}.
|
34
|
+
OPERATING_SYSTEM_NAMES = [
|
35
|
+
'FreeBSD',
|
36
|
+
'Linux',
|
37
|
+
'Mac OS X',
|
38
|
+
'Microsoft Windows',
|
39
|
+
'NetBSD',
|
40
|
+
'OpenBSD',
|
41
|
+
'Unknown',
|
42
|
+
'VMWare'
|
43
|
+
]
|
14
44
|
|
15
45
|
# Fields searched for the search scope
|
16
46
|
SEARCH_FIELDS = [
|
17
47
|
'address::text',
|
18
|
-
'
|
19
|
-
'
|
48
|
+
'comments',
|
49
|
+
'mac',
|
50
|
+
'name',
|
20
51
|
'os_flavor',
|
52
|
+
'os_name',
|
21
53
|
'os_sp',
|
22
|
-
'
|
23
|
-
|
24
|
-
|
54
|
+
'purpose'
|
55
|
+
]
|
56
|
+
|
57
|
+
# Valid values for {#state}.
|
58
|
+
STATES = [
|
59
|
+
'alive',
|
60
|
+
'down',
|
61
|
+
'unknown'
|
25
62
|
]
|
26
63
|
|
27
64
|
#
|
28
|
-
#
|
65
|
+
# Associations
|
29
66
|
#
|
30
67
|
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
68
|
+
# @!attribute [rw] exploit_attempts
|
69
|
+
# Attempts to run exploits against this host.
|
70
|
+
#
|
71
|
+
# @return [Array<Mdm::ExploitAttempt]
|
72
|
+
has_many :exploit_attempts,
|
73
|
+
:class_name => 'Mdm::ExploitAttempt',
|
74
|
+
:dependent => :destroy
|
75
|
+
|
76
|
+
# @!attribute [rw] exploited_hosts
|
77
|
+
# @todo https://www.pivotaltracker.com/story/show/48993731
|
78
|
+
# @return [Array<Mdm::ExploitedHost>]
|
79
|
+
has_many :exploited_hosts, :class_name => 'Mdm::ExploitedHost', :dependent => :destroy
|
80
|
+
|
81
|
+
# @!attribute [rw] host_details
|
82
|
+
# @return [Array<Mdm::HostDetail>]
|
83
|
+
has_many :host_details, :class_name => 'Mdm::HostDetail', :dependent => :destroy
|
84
|
+
|
85
|
+
# @!attribute [rw] hosts_tags
|
86
|
+
# A join model between {Mdm::Tag} and {Mdm::Host}. Use {#tags} to get the actual {Mdm::Tag Mdm::Tags} on this host.
|
87
|
+
# {#hosts_tags} are cleaned up in a before_destroy: {#cleanup_tags}.
|
88
|
+
#
|
89
|
+
# @todo https://www.pivotaltracker.com/story/show/48923201
|
90
|
+
# @return [Array<Mdm::HostTag>]
|
36
91
|
has_many :hosts_tags, :class_name => 'Mdm::HostTag'
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
92
|
+
|
93
|
+
# @!attribute [rw] loots
|
94
|
+
# Loot gathered from the host with {Mdm::Loot#created_at newest loot} first.
|
95
|
+
#
|
96
|
+
# @todo https://www.pivotaltracker.com/story/show/48991525
|
97
|
+
# @return [Array<Mdm::Loot>]
|
98
|
+
has_many :loots, :class_name => 'Mdm::Loot', :dependent => :destroy, :order => 'loots.created_at DESC'
|
99
|
+
|
100
|
+
# @!attribute [rw] notes
|
101
|
+
# Notes about the host entered by a user with {Mdm::Note#created_at oldest notes} first.
|
102
|
+
#
|
103
|
+
# @return [Array<Mdm::Note>]
|
104
|
+
has_many :notes, :class_name => 'Mdm::Note', :dependent => :delete_all, :order => 'notes.created_at'
|
105
|
+
|
106
|
+
# @!attribute [rw] services
|
107
|
+
# The services running on {Mdm::Service#port ports} on the host with services ordered by {Mdm::Service#port port}
|
108
|
+
# and {Mdm::Service#proto protocol}.
|
109
|
+
#
|
110
|
+
# @return [Array<Mdm::Service>]
|
111
|
+
has_many :services, :class_name => 'Mdm::Service', :dependent => :destroy, :order => 'services.port, services.proto'
|
112
|
+
|
113
|
+
# @!attribute [rw] sessions
|
114
|
+
# Sessions that are open or previously were open on the host ordered by {Mdm::Session#opened_at when the session was
|
115
|
+
# opened}
|
116
|
+
#
|
117
|
+
# @return [Array<Mdm::Session]
|
118
|
+
has_many :sessions, :class_name => 'Mdm::Session', :dependent => :destroy, :order => 'sessions.opened_at'
|
119
|
+
|
120
|
+
# @!attribute [rw] vulns
|
121
|
+
# Vulnerabilities found on the host.
|
122
|
+
#
|
123
|
+
# @return [Array<Mdm::Vuln>]
|
124
|
+
has_many :vulns, :class_name => 'Mdm::Vuln', :dependent => :delete_all
|
125
|
+
|
126
|
+
# @!attribute [rw] workspace
|
127
|
+
# The workspace in which this host was found.
|
128
|
+
#
|
129
|
+
# @return [Mdm::Workspace]
|
42
130
|
belongs_to :workspace, :class_name => 'Mdm::Workspace'
|
43
131
|
|
44
132
|
#
|
45
133
|
# Through host_tags
|
46
134
|
#
|
47
|
-
|
135
|
+
|
136
|
+
# @!attribute [r] tags
|
137
|
+
# The tags on this host. Tags are used to filter hosts.
|
138
|
+
#
|
139
|
+
# @return [Array<Mdm::Tag>]
|
140
|
+
# @see #hosts_tags
|
141
|
+
has_many :tags, :class_name => 'Mdm::Tag', :through => :hosts_tags
|
48
142
|
|
49
143
|
#
|
50
144
|
# Through services
|
51
145
|
#
|
52
|
-
|
53
|
-
|
54
|
-
|
146
|
+
|
147
|
+
# @!attribute [r] creds
|
148
|
+
# Credentials captured from {#services}.
|
149
|
+
#
|
150
|
+
# @return [Array<Mdm::Cred>]
|
151
|
+
# @see #services
|
152
|
+
has_many :creds, :class_name => 'Mdm::Cred', :through => :services
|
153
|
+
|
154
|
+
# @!attribute [r] service_notes
|
155
|
+
# {Mdm::Note Notes} about {#services} running on this host.
|
156
|
+
#
|
157
|
+
# @return [Array<Mdm::Note>]
|
158
|
+
# @see #services
|
159
|
+
has_many :service_notes, :class_name => 'Mdm::Note', :through => :services
|
160
|
+
|
161
|
+
# @!attribute [r] web_sites
|
162
|
+
# {Mdm::WebSite Web sites} running on top of {#services} on this host.
|
163
|
+
#
|
164
|
+
# @return [Array<Mdm::WebSite>]
|
165
|
+
# @see services
|
166
|
+
has_many :web_sites, :class_name => 'Mdm::WebSite', :through => :services
|
167
|
+
|
168
|
+
#
|
169
|
+
# Through vulns
|
170
|
+
#
|
171
|
+
|
172
|
+
# @!attribute [r] vuln_refs
|
173
|
+
# Join model between {#vulns} and {#refs}. Use either of those asssociations instead of this join model.
|
174
|
+
#
|
175
|
+
# @todo https://www.pivotaltracker.com/story/show/49004623
|
176
|
+
# @return [Array<Mdm::VulnRef>]
|
177
|
+
# @see #refs
|
178
|
+
# @see #vulns
|
179
|
+
has_many :vuln_refs, :class_name => 'Mdm::VulnRef', :source => :vulns_refs, :through => :vulns
|
180
|
+
|
181
|
+
#
|
182
|
+
# Through vuln_refs
|
183
|
+
#
|
184
|
+
|
185
|
+
# @!attribute [r] refs
|
186
|
+
# External references, such as CVE, to vulnerabilities found on this host.
|
187
|
+
#
|
188
|
+
# @return [Array<Mdm::Ref>]
|
189
|
+
# @see #vuln_refs
|
190
|
+
has_many :refs, :class_name => 'Mdm::Ref', :through => :vuln_refs
|
191
|
+
|
192
|
+
#
|
193
|
+
# Through refs
|
194
|
+
#
|
195
|
+
|
196
|
+
# @!attribute [r] module_refs
|
197
|
+
# {Mdm::Module::Ref References for modules} for {Mdm::Ref references for vulnerabilities}.
|
198
|
+
#
|
199
|
+
# @return [Array<Mdm::Module::Ref>]
|
200
|
+
has_many :module_refs, :class_name => 'Mdm::Module::Ref', :through => :refs
|
201
|
+
|
202
|
+
#
|
203
|
+
# Through module_refs
|
204
|
+
#
|
205
|
+
|
206
|
+
# @!attribute [r] module_details
|
207
|
+
# {Mdm::Module::Detail Details about modules} that were used to find {#vulns vulnerabilities} on this host.
|
208
|
+
#
|
209
|
+
# @return [Array<Mdm::Module::Detail]
|
210
|
+
has_many :module_details,
|
211
|
+
:class_name => 'Mdm::Module::Detail',
|
212
|
+
:source =>:detail,
|
213
|
+
:through => :module_refs,
|
214
|
+
:uniq => true
|
215
|
+
|
216
|
+
#
|
217
|
+
# Attributes
|
218
|
+
#
|
219
|
+
|
220
|
+
# @!attribute [rw] address
|
221
|
+
# The IP address of this host.
|
222
|
+
#
|
223
|
+
# @return [String]
|
224
|
+
|
225
|
+
# @!attribute [rw] arch
|
226
|
+
# The architecture of the host's CPU OR the programming language for virtual machine programming language like
|
227
|
+
# Ruby, PHP, and Java.
|
228
|
+
#
|
229
|
+
# @return [String] an element of {ARCHITECTURES}
|
230
|
+
|
231
|
+
# @!attribute [rw] comm
|
232
|
+
# @todo https://www.pivotaltracker.com/story/show/49722411
|
233
|
+
#
|
234
|
+
# @return [String]
|
235
|
+
|
236
|
+
# @!attribute [rw] comments
|
237
|
+
# User supplied comments about host.
|
238
|
+
#
|
239
|
+
# @return [String]
|
240
|
+
|
241
|
+
# @!attribute [rw] created_at
|
242
|
+
# When this host was created in the database.
|
243
|
+
#
|
244
|
+
# @return [DateTime]
|
245
|
+
|
246
|
+
# @!attribute [rw] cred_count
|
247
|
+
# Counter cache for {#creds}.
|
248
|
+
#
|
249
|
+
# @return [Integer]
|
250
|
+
|
251
|
+
# @!attribute [rw] exploit_attempt_count
|
252
|
+
# Counter cache for {#exploit_attempts}.
|
253
|
+
#
|
254
|
+
# @return [Integer]
|
255
|
+
|
256
|
+
# @!attribute [rw] host_detail_count
|
257
|
+
# Counter cache for {#host_details}.
|
258
|
+
#
|
259
|
+
# @return [Integer]
|
260
|
+
|
261
|
+
# @!attribute [rw] info
|
262
|
+
# Information about this host gathered from the host.
|
263
|
+
#
|
264
|
+
# @return [String]
|
265
|
+
|
266
|
+
# @!attribute [rw] mac
|
267
|
+
# The MAC address of this host.
|
268
|
+
#
|
269
|
+
# @return [String]
|
270
|
+
# @see http://en.wikipedia.org/wiki/Mac_address
|
271
|
+
|
272
|
+
# @!attribute [rw] name
|
273
|
+
# The name of the host. If the host name is not available, then it will just be the IP address.
|
274
|
+
#
|
275
|
+
# @return [String]
|
276
|
+
|
277
|
+
# @!attribute [rw] note_count
|
278
|
+
# Counter cache for {#notes}.
|
279
|
+
#
|
280
|
+
# @return [Integer]
|
281
|
+
|
282
|
+
# @!attribute [rw] os_flavor
|
283
|
+
# The flavor of {#os_name}.
|
284
|
+
#
|
285
|
+
# @example Windows XP
|
286
|
+
# host.os_name = 'Microsoft Windows'
|
287
|
+
# host.os_flavor = 'XP'
|
288
|
+
#
|
289
|
+
# @return [String]
|
290
|
+
|
291
|
+
# @!attribute [rw] os_lang
|
292
|
+
# Free-form language of operating system. Usually either spelled out like 'English' or an
|
293
|
+
# {http://en.wikipedia.org/wiki/IETF_language_tag IETF language tag} like 'en' or 'en-US'.
|
294
|
+
#
|
295
|
+
# @return [String]
|
296
|
+
|
297
|
+
# @!attribute [rw] os_name
|
298
|
+
# The name of the operating system.
|
299
|
+
#
|
300
|
+
# @return [String] an element of {OPERATING_SYSTEM_NAMES}.
|
301
|
+
|
302
|
+
# @!attribute [rw] os_sp
|
303
|
+
# The service pack of the {#os_flavor} of the {#os_name}.
|
304
|
+
#
|
305
|
+
# @example Windows XP SP2
|
306
|
+
# host.os_name = 'Microsoft Windows'
|
307
|
+
# host.os_flavor = 'XP'
|
308
|
+
# host.os_sp = 'SP2'
|
309
|
+
#
|
310
|
+
# @return [String]
|
311
|
+
|
312
|
+
# @!attribute [rw] purpose
|
313
|
+
# The purpose of the host on the network, such as 'client' or 'firewall'.
|
314
|
+
#
|
315
|
+
# @return [String]
|
316
|
+
|
317
|
+
# @!attribute [rw] scope
|
318
|
+
# Interface identifier for link-local IPv6
|
319
|
+
#
|
320
|
+
# @return [String]
|
321
|
+
# @see http://en.wikipedia.org/wiki/IPv6_address#Link-local_addresses_and_zone_indices
|
322
|
+
|
323
|
+
# @!attribute [rw] service_count
|
324
|
+
# Counter cache for {#services}.
|
325
|
+
#
|
326
|
+
# @return [Integer]
|
327
|
+
|
328
|
+
# @!attribute [rw] state
|
329
|
+
# Whether the host is alive, down, or in an unknown state.
|
330
|
+
#
|
331
|
+
# @return [String] element of {STATES}.
|
332
|
+
|
333
|
+
# @!attribute [rw] updated_at
|
334
|
+
# The last time this host was updated in the database.
|
335
|
+
#
|
336
|
+
# @return [DateTime]
|
337
|
+
|
338
|
+
# @!attribute [rw] virtual_host
|
339
|
+
# The name of the virtual machine host software, such as 'VMWare', 'QEMU', 'XEN', etc.
|
340
|
+
#
|
341
|
+
# @return [String]
|
342
|
+
|
343
|
+
# @!attribute [rw] vuln_count
|
344
|
+
# Counter cache for {#vulns}.
|
345
|
+
#
|
346
|
+
# @return [Integer]
|
347
|
+
|
348
|
+
#
|
349
|
+
# Callbacks
|
350
|
+
#
|
351
|
+
|
352
|
+
before_destroy :cleanup_tags
|
55
353
|
|
56
354
|
#
|
57
355
|
# Nested Attributes
|
@@ -74,6 +372,21 @@ class Mdm::Host < ActiveRecord::Base
|
|
74
372
|
:scope => :workspace_id,
|
75
373
|
:unless => :ip_address_invalid?
|
76
374
|
}
|
375
|
+
validates :arch,
|
376
|
+
:allow_nil => true,
|
377
|
+
:inclusion => {
|
378
|
+
:in => ARCHITECTURES
|
379
|
+
}
|
380
|
+
validates :os_name,
|
381
|
+
:allow_nil => true,
|
382
|
+
:inclusion => {
|
383
|
+
:in => OPERATING_SYSTEM_NAMES
|
384
|
+
}
|
385
|
+
validates :state,
|
386
|
+
:allow_nil => true,
|
387
|
+
:inclusion => {
|
388
|
+
:in => STATES
|
389
|
+
}
|
77
390
|
validates :workspace, :presence => true
|
78
391
|
|
79
392
|
#
|
@@ -100,11 +413,18 @@ class Mdm::Host < ActiveRecord::Base
|
|
100
413
|
scope :tag_search,
|
101
414
|
lambda { |*args| where("tags.name" => args[0]).includes(:tags) }
|
102
415
|
|
416
|
+
# Returns whether 'host.updated.<attr>' {#notes note} is {Mdm::Note#data locked}.
|
417
|
+
#
|
418
|
+
# @return [true] if Mdm::Note with 'host.updated.<attr>' as {Mdm::Note#name} exists and data[:locked] is `true`.
|
419
|
+
# @return [false] otherwise.
|
103
420
|
def attribute_locked?(attr)
|
104
421
|
n = notes.find_by_ntype("host.updated.#{attr}")
|
105
422
|
n && n.data[:locked]
|
106
423
|
end
|
107
424
|
|
425
|
+
# Destroys any {Mdm::Tag Mdm::Tags} that will have no {Mdm::Tag#hosts} left after this host is deleted.
|
426
|
+
#
|
427
|
+
# @return [void]
|
108
428
|
def cleanup_tags
|
109
429
|
# No need to keep tags with no hosts
|
110
430
|
tags.each do |tag|
|
@@ -116,6 +436,8 @@ class Mdm::Host < ActiveRecord::Base
|
|
116
436
|
|
117
437
|
# This is replicated by the IpAddressValidator class. Had to put it here as well to avoid
|
118
438
|
# SQL errors when checking address uniqueness.
|
439
|
+
#
|
440
|
+
# @return [void]
|
119
441
|
def ip_address_invalid?
|
120
442
|
begin
|
121
443
|
potential_ip = IPAddr.new(address)
|
@@ -125,6 +447,10 @@ class Mdm::Host < ActiveRecord::Base
|
|
125
447
|
end
|
126
448
|
end
|
127
449
|
|
450
|
+
# Returns whether this host is a virtual machine.
|
451
|
+
#
|
452
|
+
# @return [true] unless {#virtual_host} is `nil`.
|
453
|
+
# @return [false] otherwise.
|
128
454
|
def is_vm?
|
129
455
|
!!self.virtual_host
|
130
456
|
end
|