metasploit_data_models 0.7.0 → 0.11.2
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.
- 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 +78 -45
- 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
|