fog-kubevirt 0.2.1 → 0.3.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/lib/fog/compute/kubevirt.rb +24 -6
- data/lib/fog/compute/kubevirt/models/persistentvolume.rb +82 -0
- data/lib/fog/compute/kubevirt/models/persistentvolumes.rb +73 -0
- data/lib/fog/compute/kubevirt/models/storageclass.rb +33 -0
- data/lib/fog/compute/kubevirt/models/storageclasses.rb +61 -0
- data/lib/fog/compute/kubevirt/models/vm_base.rb +3 -2
- data/lib/fog/compute/kubevirt/models/vm_data.rb +11 -19
- data/lib/fog/compute/kubevirt/models/vminstance.rb +3 -2
- data/lib/fog/compute/kubevirt/models/vms.rb +49 -19
- data/lib/fog/compute/kubevirt/models/volume.rb +44 -69
- data/lib/fog/compute/kubevirt/models/volumes.rb +3 -58
- data/lib/fog/compute/kubevirt/requests/{create_volume.rb → create_persistentvolume.rb} +2 -2
- data/lib/fog/compute/kubevirt/requests/create_storageclass.rb +18 -0
- data/lib/fog/compute/kubevirt/requests/{delete_volume.rb → delete_persistentvolume.rb} +2 -2
- data/lib/fog/compute/kubevirt/requests/{get_volume.rb → delete_storageclass.rb} +3 -3
- data/lib/fog/compute/kubevirt/requests/delete_vm.rb +1 -1
- data/lib/fog/compute/kubevirt/requests/delete_vminstance.rb +3 -3
- data/lib/fog/compute/kubevirt/requests/get_persistentvolume.rb +16 -0
- data/lib/fog/compute/kubevirt/requests/get_storageclass.rb +16 -0
- data/lib/fog/compute/kubevirt/requests/list_persistentvolumes.rb +22 -0
- data/lib/fog/compute/kubevirt/requests/list_storageclasses.rb +22 -0
- data/lib/fog/compute/kubevirt/requests/list_volumes.rb +13 -5
- data/lib/fog/kubevirt/version.rb +1 -1
- data/spec/create_vm_spec.rb +101 -0
- data/spec/fixtures/kubevirt/{volume/volumes_crud.yml → persistentvolume/persistent_volumes_crud.yml} +0 -0
- data/spec/fixtures/kubevirt/pvc/pvcs_crud.yml +122 -76
- data/spec/fixtures/kubevirt/storageclass/storageclasses_crud.yml +400 -0
- data/spec/fixtures/kubevirt/vm/vm_create_multi.yml +459 -0
- data/spec/fixtures/kubevirt/vm/vm_create_single.yml +362 -0
- data/spec/{volumes_v1alpha2_spec.rb → persistent_volumes_v1alpha2_spec.rb} +11 -11
- data/spec/pvcs_v1alpha2_spec.rb +7 -0
- data/spec/storage_classes_v1_spec.rb +46 -0
- metadata +28 -9
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 25a9419e80245eff2f02bac207711317b6503432
|
4
|
+
data.tar.gz: 530e0f33df50161f668f78bc57d25b00c1b10951
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f5c986661f483f6c05e9b20f4b8d7ce2b04fa4e079d6b0e228d6fe69698e9ed9197c072a1e49661b87cff6d76a8782cdaf9e46fe30d54053dd0ddf3db95cfe4c
|
7
|
+
data.tar.gz: 807de8ea60c22dde2538805218be5eb80105b081701cb65ad470636a9f01012d4d3c209b860092ba9ecc1e96c3f5c715690d7e679298a7ab7d7cd0eb86123b87
|
data/lib/fog/compute/kubevirt.rb
CHANGED
@@ -19,12 +19,16 @@ module Fog
|
|
19
19
|
collection :nodes
|
20
20
|
model :vm
|
21
21
|
collection :vms
|
22
|
+
model :persistentvolume
|
23
|
+
collection :persistentvolumes
|
22
24
|
model :pvc
|
23
25
|
collection :pvcs
|
24
26
|
model :server
|
25
27
|
collection :servers
|
26
28
|
model :service
|
27
29
|
collection :services
|
30
|
+
model :storageclass
|
31
|
+
collection :storageclasses
|
28
32
|
model :template
|
29
33
|
collection :templates
|
30
34
|
model :volume
|
@@ -34,33 +38,38 @@ module Fog
|
|
34
38
|
request :create_networkattachmentdef
|
35
39
|
request :create_vm
|
36
40
|
request :create_vminstance
|
41
|
+
request :create_persistentvolume
|
37
42
|
request :create_pvc
|
38
43
|
request :create_service
|
39
|
-
request :
|
44
|
+
request :create_storageclass
|
40
45
|
request :delete_networkattachmentdef
|
46
|
+
request :delete_persistentvolume
|
41
47
|
request :delete_pvc
|
42
48
|
request :delete_service
|
49
|
+
request :delete_storageclass
|
43
50
|
request :delete_vminstance
|
44
51
|
request :delete_vm
|
45
|
-
request :delete_volume
|
46
52
|
request :get_vminstance
|
47
53
|
request :get_networkattachmentdef
|
48
54
|
request :get_node
|
55
|
+
request :get_persistentvolume
|
49
56
|
request :get_pvc
|
50
57
|
request :get_vm
|
51
|
-
request :get_volume
|
52
58
|
request :get_server
|
53
59
|
request :get_service
|
60
|
+
request :get_storageclass
|
54
61
|
request :get_template
|
55
62
|
request :list_vminstances
|
56
63
|
request :list_nodes
|
57
64
|
request :list_networkattachmentdefs
|
58
65
|
request :list_vms
|
66
|
+
request :list_persistentvolumes
|
59
67
|
request :list_pvcs
|
60
|
-
request :list_volumes
|
61
68
|
request :list_servers
|
62
69
|
request :list_services
|
70
|
+
request :list_storageclasses
|
63
71
|
request :list_templates
|
72
|
+
request :list_volumes
|
64
73
|
request :update_vm
|
65
74
|
|
66
75
|
module Shared
|
@@ -167,6 +176,11 @@ module Fog
|
|
167
176
|
#
|
168
177
|
NETWORK_GROUP = 'k8s.cni.cncf.io'.freeze
|
169
178
|
|
179
|
+
#
|
180
|
+
# The API group of the Kubernetes network extention:
|
181
|
+
#
|
182
|
+
STORAGE_GROUP = 'storage.k8s.io'.freeze
|
183
|
+
|
170
184
|
def initialize(options={})
|
171
185
|
require 'kubeclient'
|
172
186
|
|
@@ -355,7 +369,7 @@ module Fog
|
|
355
369
|
create_client_from_config(path)
|
356
370
|
else
|
357
371
|
create_client_from_token(url)
|
358
|
-
end
|
372
|
+
end
|
359
373
|
end
|
360
374
|
|
361
375
|
def create_client_from_token(url)
|
@@ -429,7 +443,7 @@ module Fog
|
|
429
443
|
# version detected based on
|
430
444
|
# https://github.com/kubernetes-incubator/apiserver-builder/blob/master/docs/concepts/aggregation.md#viewing-discovery-information
|
431
445
|
preferredVersion = response["preferredVersion"]
|
432
|
-
return preferredVersion["version"] if preferredVersion
|
446
|
+
return preferredVersion["version"] if preferredVersion
|
433
447
|
response["versions"][0]
|
434
448
|
end
|
435
449
|
|
@@ -449,6 +463,10 @@ module Fog
|
|
449
463
|
create_client('/apis/' + NETWORK_GROUP)
|
450
464
|
end
|
451
465
|
|
466
|
+
def kube_storage_client
|
467
|
+
create_client('/apis/' + STORAGE_GROUP)
|
468
|
+
end
|
469
|
+
|
452
470
|
def log
|
453
471
|
@log
|
454
472
|
end
|
@@ -0,0 +1,82 @@
|
|
1
|
+
module Fog
|
2
|
+
module Compute
|
3
|
+
class Kubevirt
|
4
|
+
class Persistentvolume < Fog::Model
|
5
|
+
identity :name
|
6
|
+
|
7
|
+
attribute :resource_version, :aliases => 'metadata_resource_version'
|
8
|
+
attribute :uid, :aliases => 'metadata_uid'
|
9
|
+
attribute :annotations, :aliases => 'metadata_annotations'
|
10
|
+
attribute :labels, :aliases => 'metadata_labels'
|
11
|
+
attribute :access_modes, :aliases => 'spec_access_modes'
|
12
|
+
attribute :mount_options, :aliases => 'spec_mount_options'
|
13
|
+
attribute :reclaim_policy, :aliases => 'spec_reclaim_policy'
|
14
|
+
attribute :storage_class, :aliases => 'spec_storage_class'
|
15
|
+
attribute :capacity, :aliases => 'spec_capacity'
|
16
|
+
attribute :claim_ref, :aliases => 'spec_claim_ref'
|
17
|
+
attribute :type, :aliases => 'spec_type'
|
18
|
+
attribute :config, :aliases => 'spec_config'
|
19
|
+
attribute :phase, :aliases => 'status_phase'
|
20
|
+
attribute :reason, :aliases => 'status_reason'
|
21
|
+
attribute :message, :aliases => 'status_message'
|
22
|
+
|
23
|
+
def self.parse(object)
|
24
|
+
metadata = object[:metadata]
|
25
|
+
spec = object[:spec]
|
26
|
+
status = object[:status]
|
27
|
+
type = detect_type(spec)
|
28
|
+
|
29
|
+
{
|
30
|
+
:name => metadata[:name],
|
31
|
+
:resource_version => metadata[:resourceVersion],
|
32
|
+
:uid => metadata[:uid],
|
33
|
+
:annotations => metadata[:annotations],
|
34
|
+
:labels => metadata[:labels],
|
35
|
+
:access_modes => spec[:accessModes],
|
36
|
+
:mount_options => spec[:mountOptions],
|
37
|
+
:reclaim_policy => spec[:persistentVolumeReclaimPolicy],
|
38
|
+
:storage_class => spec[:storageClassName],
|
39
|
+
:capacity => spec.dig(:capacity, :storage),
|
40
|
+
:claim_ref => spec[:claimRef],
|
41
|
+
:type => type,
|
42
|
+
:config => spec[type&.to_sym],
|
43
|
+
:phase => status[:phase],
|
44
|
+
:reason => status[:reason],
|
45
|
+
:message => status[:message]
|
46
|
+
}
|
47
|
+
end
|
48
|
+
|
49
|
+
def self.detect_type(spec)
|
50
|
+
type = ''
|
51
|
+
type = 'awsElasticBlockStore' if spec.keys.include?(:awsElasticBlockStore)
|
52
|
+
type = 'azureDisk' if spec.keys.include?(:azureDisk)
|
53
|
+
type = 'azureFile' if spec.keys.include?(:azureFile)
|
54
|
+
type = 'cephfs' if spec.keys.include?(:cephfs)
|
55
|
+
type = 'configMap' if spec.keys.include?(:configMap)
|
56
|
+
type = 'csi' if spec.keys.include?(:csi)
|
57
|
+
type = 'downwardAPI' if spec.keys.include?(:downwardAPI)
|
58
|
+
type = 'emptyDir' if spec.keys.include?(:emptyDir)
|
59
|
+
type = 'fc' if spec.keys.include?(:fc)
|
60
|
+
type = 'flexVolume' if spec.keys.include?(:flexVolume)
|
61
|
+
type = 'flocker' if spec.keys.include?(:flocker)
|
62
|
+
type = 'gcePersistentDisk' if spec.keys.include?(:gcePersistentDisk)
|
63
|
+
type = 'glusterfs' if spec.keys.include?(:glusterfs)
|
64
|
+
type = 'hostPath' if spec.keys.include?(:hostPath)
|
65
|
+
type = 'iscsi' if spec.keys.include?(:iscsi)
|
66
|
+
type = 'local' if spec.keys.include?(:local)
|
67
|
+
type = 'nfs' if spec.keys.include?(:nfs)
|
68
|
+
type = 'persistentVolumeClaim' if spec.keys.include?(:persistentVolumeClaim)
|
69
|
+
type = 'projected' if spec.keys.include?(:projected)
|
70
|
+
type = 'portworxVolume' if spec.keys.include?(:portworxVolume)
|
71
|
+
type = 'quobyte' if spec.keys.include?(:quobyte)
|
72
|
+
type = 'rbd' if spec.keys.include?(:rbd)
|
73
|
+
type = 'scaleIO' if spec.keys.include?(:scaleIO)
|
74
|
+
type = 'secret' if spec.keys.include?(:secret)
|
75
|
+
type = 'storageos' if spec.keys.include?(:storageos)
|
76
|
+
type = 'vsphereVolume' if spec.keys.include?(:vsphereVolume)
|
77
|
+
type
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
require 'fog/core/collection'
|
2
|
+
require 'fog/compute/kubevirt/models/persistentvolume'
|
3
|
+
|
4
|
+
module Fog
|
5
|
+
module Compute
|
6
|
+
class Kubevirt
|
7
|
+
class Persistentvolumes < Fog::Collection
|
8
|
+
attr_reader :kind, :resource_version
|
9
|
+
|
10
|
+
model Fog::Compute::Kubevirt::Persistentvolume
|
11
|
+
|
12
|
+
def all(filters = {})
|
13
|
+
volumes = service.list_persistentvolumes(filters)
|
14
|
+
@kind = volumes.kind
|
15
|
+
@resource_version = volumes.resource_version
|
16
|
+
load volumes
|
17
|
+
end
|
18
|
+
|
19
|
+
def get(name)
|
20
|
+
new service.get_persistentvolume(name)
|
21
|
+
end
|
22
|
+
|
23
|
+
# Creates a volume using provided paramters:
|
24
|
+
# :name [String] - name of a volume
|
25
|
+
# :labels [Hash] - a hash of key,values representing the labels
|
26
|
+
# :storage_class [String] - the storage class name of the volume
|
27
|
+
# :capacity [String] - The capacity of the storage if applied
|
28
|
+
# :accessModes [Arr] - the access modes for the volume, values are specified here:
|
29
|
+
# https://kubernetes.io/docs/concepts/storage/persistent-volumes/#access-modes
|
30
|
+
# :type [String] - the type of the storage
|
31
|
+
# :config [Hash] - storage specific configuration to be applied for the volume
|
32
|
+
# correlated to the args[:type]
|
33
|
+
# @param [Hash] attributes containing details about volume to be created.
|
34
|
+
def create(args = {})
|
35
|
+
name = args[:name]
|
36
|
+
labels = args[:labels]
|
37
|
+
|
38
|
+
volume = {
|
39
|
+
:apiVersion => "v1",
|
40
|
+
:kind => "PersistentVolume",
|
41
|
+
:metadata => {
|
42
|
+
:name => name
|
43
|
+
},
|
44
|
+
:spec => {
|
45
|
+
:storageClassName => args[:storage_class]
|
46
|
+
}
|
47
|
+
}
|
48
|
+
|
49
|
+
volume[:metadata][:labels] = labels if labels
|
50
|
+
volume[:spec][:capacity] = {
|
51
|
+
:storage => args[:capacity]
|
52
|
+
} if args[:capacity]
|
53
|
+
|
54
|
+
volume[:spec][:accessModes] = args[:access_modes] if args[:access_modes]
|
55
|
+
volume[:spec][args[:type].to_sym] = args[:config] if args[:type]
|
56
|
+
|
57
|
+
service.create_persistentvolume(volume)
|
58
|
+
end
|
59
|
+
|
60
|
+
def delete(name)
|
61
|
+
begin
|
62
|
+
volume = get(name)
|
63
|
+
rescue ::Fog::Kubevirt::Errors::ClientError
|
64
|
+
# the volume doesn't exist
|
65
|
+
volume = nil
|
66
|
+
end
|
67
|
+
|
68
|
+
service.delete_persistentvolume(name) unless volume.nil?
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module Fog
|
2
|
+
module Compute
|
3
|
+
class Kubevirt
|
4
|
+
class Storageclass < Fog::Model
|
5
|
+
# Metadata
|
6
|
+
identity :name
|
7
|
+
attribute :resource_version, :aliases => 'metadata_resource_version'
|
8
|
+
attribute :uid, :aliases => 'metadata_uid'
|
9
|
+
|
10
|
+
attribute :mount_options
|
11
|
+
attribute :parameters
|
12
|
+
attribute :provisioner
|
13
|
+
attribute :reclaim_policy
|
14
|
+
attribute :volume_binding_mode
|
15
|
+
|
16
|
+
def self.parse(object)
|
17
|
+
metadata = object[:metadata]
|
18
|
+
{
|
19
|
+
:name => metadata[:name],
|
20
|
+
:resource_version => metadata[:resourceVersion],
|
21
|
+
:uid => metadata[:uid],
|
22
|
+
|
23
|
+
:parameters => object[:parameters],
|
24
|
+
:mount_options => object[:mountOptions],
|
25
|
+
:provisioner => object[:provisioner],
|
26
|
+
:reclaim_policy => object[:reclaimPolicy],
|
27
|
+
:volume_binding_mode => object[:volumeBindingMode]
|
28
|
+
}
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
require 'fog/core/collection'
|
2
|
+
require 'fog/compute/kubevirt/models/storageclass'
|
3
|
+
|
4
|
+
module Fog
|
5
|
+
module Compute
|
6
|
+
class Kubevirt
|
7
|
+
class Storageclasses < Fog::Collection
|
8
|
+
attr_reader :kind, :resource_version
|
9
|
+
|
10
|
+
model Fog::Compute::Kubevirt::Storageclass
|
11
|
+
|
12
|
+
def all(filters = {})
|
13
|
+
storageclasses = service.list_storageclasses(filters)
|
14
|
+
@kind = storageclasses.kind
|
15
|
+
@resource_version = storageclasses.resource_version
|
16
|
+
load storageclasses
|
17
|
+
end
|
18
|
+
|
19
|
+
def get(name)
|
20
|
+
new service.get_storageclass(name)
|
21
|
+
end
|
22
|
+
|
23
|
+
# Creates a storage class using provided paramters:
|
24
|
+
# https://kubernetes.io/docs/concepts/storage/storage-classes
|
25
|
+
# :name [String] - name of a storage class
|
26
|
+
# :parameters [Object] - parameters for the provisioner that should create volumes of this storage class
|
27
|
+
# :mount_options [Arr] - mount options for the dynamically provisioned PersistentVolumes of this storage class.
|
28
|
+
# :provisioner [String] - the type of the provisioner
|
29
|
+
# :volume_binding_mode [String] - indicates how PersistentVolumeClaims should be provisioned and bound
|
30
|
+
# :reclaim_policy [String] - the reclaim policy of the created PVs (Defaults to Delete).
|
31
|
+
def create(args = {})
|
32
|
+
storageclass = {
|
33
|
+
:apiVersion => "storage.k8s.io/v1",
|
34
|
+
:kind => "StorageClass",
|
35
|
+
:metadata => {
|
36
|
+
:name => args[:name],
|
37
|
+
},
|
38
|
+
:parameters => args[:parameters],
|
39
|
+
:mount_options => args[:mount_options],
|
40
|
+
:provisioner => args[:provisioner],
|
41
|
+
:volume_binding_mode => args[:volume_binding_mode],
|
42
|
+
:reclaim_policy => args[:reclaim_policy]
|
43
|
+
}
|
44
|
+
|
45
|
+
service.create_storageclass(storageclass)
|
46
|
+
end
|
47
|
+
|
48
|
+
def delete(name)
|
49
|
+
begin
|
50
|
+
storageclass = get(name)
|
51
|
+
rescue ::Fog::Kubevirt::Errors::ClientError
|
52
|
+
# the storageclass doesn't exist
|
53
|
+
storageclass = nil
|
54
|
+
end
|
55
|
+
|
56
|
+
service.delete_storageclass(name) unless storageclass.nil?
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
@@ -33,14 +33,15 @@ module Fog
|
|
33
33
|
annotations = metadata[:annotations]
|
34
34
|
cpu = domain[:cpu]
|
35
35
|
mem = domain.dig(:resources, :requests, :memory)
|
36
|
+
disks = parse_disks(domain[:devices][:disks])
|
36
37
|
vm = {
|
37
38
|
:namespace => metadata[:namespace],
|
38
39
|
:name => metadata[:name],
|
39
40
|
:resource_version => metadata[:resourceVersion],
|
40
41
|
:uid => metadata[:uid],
|
41
42
|
:labels => metadata[:labels],
|
42
|
-
:disks =>
|
43
|
-
:volumes => parse_volumes(spec[:volumes]),
|
43
|
+
:disks => disks,
|
44
|
+
:volumes => parse_volumes(spec[:volumes], disks),
|
44
45
|
:interfaces => parse_interfaces(domain[:devices][:interfaces]),
|
45
46
|
:networks => parse_networks(spec[:networks]),
|
46
47
|
:status => object[:spec][:running].to_s == "true" ? "running" : "stopped",
|
@@ -1,3 +1,5 @@
|
|
1
|
+
require 'fog/compute/kubevirt/models/volume'
|
2
|
+
|
1
3
|
module Fog
|
2
4
|
module Compute
|
3
5
|
class Kubevirt
|
@@ -55,7 +57,6 @@ module Fog
|
|
55
57
|
disk = VmDisk.new
|
56
58
|
disk.name = d[:name]
|
57
59
|
disk.boot_order = d[:bootOrder]
|
58
|
-
disk.volume_name = d[:volumeName]
|
59
60
|
|
60
61
|
if d.keys.include?(:cdrom)
|
61
62
|
disk.type = 'cdrom'
|
@@ -82,12 +83,13 @@ module Fog
|
|
82
83
|
# Returns an array of parsed volumes
|
83
84
|
#
|
84
85
|
# @param object [Hash] A hash with raw volumes data.
|
86
|
+
# @param disks [Array] the disks of the vm associated to the volumes
|
85
87
|
#
|
86
|
-
def parse_volumes(object)
|
88
|
+
def parse_volumes(object, disks)
|
87
89
|
return {} if object.nil?
|
88
90
|
volumes = []
|
89
91
|
object.each do |v|
|
90
|
-
volume =
|
92
|
+
volume = Volume.new
|
91
93
|
volume.name = v[:name]
|
92
94
|
if v.keys.include?(:containerDisk)
|
93
95
|
volume.type = 'containerDisk'
|
@@ -120,6 +122,12 @@ module Fog
|
|
120
122
|
volume.type = 'configMap'
|
121
123
|
volume.info = v.dig(:configMap, :name)
|
122
124
|
end
|
125
|
+
|
126
|
+
volume.config = v[volume.type.to_sym]
|
127
|
+
disk = disks.select { |d| d.name == volume.name }.first
|
128
|
+
volume.boot_order = disk.boot_order
|
129
|
+
volume.bus = disk.bus if disk.respond_to?(:bus)
|
130
|
+
|
123
131
|
volumes << volume
|
124
132
|
end
|
125
133
|
|
@@ -143,27 +151,11 @@ module Fog
|
|
143
151
|
|
144
152
|
class VmDisk
|
145
153
|
attr_accessor :name,
|
146
|
-
:volume_name,
|
147
154
|
:boot_order,
|
148
155
|
:type, # values: cdrom, disk, floppy, lun
|
149
156
|
:bus,
|
150
157
|
:readonly
|
151
158
|
end
|
152
|
-
|
153
|
-
class VmVolume < Fog::Model
|
154
|
-
identity :name
|
155
|
-
attribute :id
|
156
|
-
# values: containerDisk, persistentVolumeClaim, emptyDisk,
|
157
|
-
# ephemeral, cloudInitNoCloud, hostDisk, secret,
|
158
|
-
# dataVolume, serviceAccount, configMap
|
159
|
-
attribute :type
|
160
|
-
|
161
|
-
attribute :info # specific piece of information per volume type
|
162
|
-
|
163
|
-
def persisted?
|
164
|
-
!name.nil?
|
165
|
-
end
|
166
|
-
end
|
167
159
|
end
|
168
160
|
end
|
169
161
|
end
|