David K. Bainbridge | 528b318 | 2017-01-23 08:51:59 -0800 | [diff] [blame] | 1 | // Copyright 2016 Canonical Ltd. |
| 2 | // Licensed under the LGPLv3, see LICENCE file for details. |
| 3 | |
| 4 | package gomaasapi |
| 5 | |
| 6 | import ( |
| 7 | "github.com/juju/errors" |
| 8 | "github.com/juju/schema" |
| 9 | "github.com/juju/version" |
| 10 | ) |
| 11 | |
| 12 | type blockdevice struct { |
| 13 | resourceURI string |
| 14 | |
| 15 | id int |
| 16 | name string |
| 17 | model string |
| 18 | path string |
| 19 | usedFor string |
| 20 | tags []string |
| 21 | |
| 22 | blockSize uint64 |
| 23 | usedSize uint64 |
| 24 | size uint64 |
| 25 | |
| 26 | partitions []*partition |
| 27 | } |
| 28 | |
| 29 | // ID implements BlockDevice. |
| 30 | func (b *blockdevice) ID() int { |
| 31 | return b.id |
| 32 | } |
| 33 | |
| 34 | // Name implements BlockDevice. |
| 35 | func (b *blockdevice) Name() string { |
| 36 | return b.name |
| 37 | } |
| 38 | |
| 39 | // Model implements BlockDevice. |
| 40 | func (b *blockdevice) Model() string { |
| 41 | return b.model |
| 42 | } |
| 43 | |
| 44 | // Path implements BlockDevice. |
| 45 | func (b *blockdevice) Path() string { |
| 46 | return b.path |
| 47 | } |
| 48 | |
| 49 | // UsedFor implements BlockDevice. |
| 50 | func (b *blockdevice) UsedFor() string { |
| 51 | return b.usedFor |
| 52 | } |
| 53 | |
| 54 | // Tags implements BlockDevice. |
| 55 | func (b *blockdevice) Tags() []string { |
| 56 | return b.tags |
| 57 | } |
| 58 | |
| 59 | // BlockSize implements BlockDevice. |
| 60 | func (b *blockdevice) BlockSize() uint64 { |
| 61 | return b.blockSize |
| 62 | } |
| 63 | |
| 64 | // UsedSize implements BlockDevice. |
| 65 | func (b *blockdevice) UsedSize() uint64 { |
| 66 | return b.usedSize |
| 67 | } |
| 68 | |
| 69 | // Size implements BlockDevice. |
| 70 | func (b *blockdevice) Size() uint64 { |
| 71 | return b.size |
| 72 | } |
| 73 | |
| 74 | // Partitions implements BlockDevice. |
| 75 | func (b *blockdevice) Partitions() []Partition { |
| 76 | result := make([]Partition, len(b.partitions)) |
| 77 | for i, v := range b.partitions { |
| 78 | result[i] = v |
| 79 | } |
| 80 | return result |
| 81 | } |
| 82 | |
| 83 | func readBlockDevices(controllerVersion version.Number, source interface{}) ([]*blockdevice, error) { |
| 84 | checker := schema.List(schema.StringMap(schema.Any())) |
| 85 | coerced, err := checker.Coerce(source, nil) |
| 86 | if err != nil { |
| 87 | return nil, WrapWithDeserializationError(err, "blockdevice base schema check failed") |
| 88 | } |
| 89 | valid := coerced.([]interface{}) |
| 90 | |
| 91 | var deserialisationVersion version.Number |
| 92 | for v := range blockdeviceDeserializationFuncs { |
| 93 | if v.Compare(deserialisationVersion) > 0 && v.Compare(controllerVersion) <= 0 { |
| 94 | deserialisationVersion = v |
| 95 | } |
| 96 | } |
| 97 | if deserialisationVersion == version.Zero { |
| 98 | return nil, NewUnsupportedVersionError("no blockdevice read func for version %s", controllerVersion) |
| 99 | } |
| 100 | readFunc := blockdeviceDeserializationFuncs[deserialisationVersion] |
| 101 | return readBlockDeviceList(valid, readFunc) |
| 102 | } |
| 103 | |
| 104 | // readBlockDeviceList expects the values of the sourceList to be string maps. |
| 105 | func readBlockDeviceList(sourceList []interface{}, readFunc blockdeviceDeserializationFunc) ([]*blockdevice, error) { |
| 106 | result := make([]*blockdevice, 0, len(sourceList)) |
| 107 | for i, value := range sourceList { |
| 108 | source, ok := value.(map[string]interface{}) |
| 109 | if !ok { |
| 110 | return nil, NewDeserializationError("unexpected value for blockdevice %d, %T", i, value) |
| 111 | } |
| 112 | blockdevice, err := readFunc(source) |
| 113 | if err != nil { |
| 114 | return nil, errors.Annotatef(err, "blockdevice %d", i) |
| 115 | } |
| 116 | result = append(result, blockdevice) |
| 117 | } |
| 118 | return result, nil |
| 119 | } |
| 120 | |
| 121 | type blockdeviceDeserializationFunc func(map[string]interface{}) (*blockdevice, error) |
| 122 | |
| 123 | var blockdeviceDeserializationFuncs = map[version.Number]blockdeviceDeserializationFunc{ |
| 124 | twoDotOh: blockdevice_2_0, |
| 125 | } |
| 126 | |
| 127 | func blockdevice_2_0(source map[string]interface{}) (*blockdevice, error) { |
| 128 | fields := schema.Fields{ |
| 129 | "resource_uri": schema.String(), |
| 130 | |
| 131 | "id": schema.ForceInt(), |
| 132 | "name": schema.String(), |
| 133 | "model": schema.OneOf(schema.Nil(""), schema.String()), |
| 134 | "path": schema.String(), |
| 135 | "used_for": schema.String(), |
| 136 | "tags": schema.List(schema.String()), |
| 137 | |
| 138 | "block_size": schema.ForceUint(), |
| 139 | "used_size": schema.ForceUint(), |
| 140 | "size": schema.ForceUint(), |
| 141 | |
| 142 | "partitions": schema.List(schema.StringMap(schema.Any())), |
| 143 | } |
| 144 | checker := schema.FieldMap(fields, nil) |
| 145 | coerced, err := checker.Coerce(source, nil) |
| 146 | if err != nil { |
| 147 | return nil, WrapWithDeserializationError(err, "blockdevice 2.0 schema check failed") |
| 148 | } |
| 149 | valid := coerced.(map[string]interface{}) |
| 150 | // From here we know that the map returned from the schema coercion |
| 151 | // contains fields of the right type. |
| 152 | |
| 153 | partitions, err := readPartitionList(valid["partitions"].([]interface{}), partition_2_0) |
| 154 | if err != nil { |
| 155 | return nil, errors.Trace(err) |
| 156 | } |
| 157 | |
| 158 | model, _ := valid["model"].(string) |
| 159 | result := &blockdevice{ |
| 160 | resourceURI: valid["resource_uri"].(string), |
| 161 | |
| 162 | id: valid["id"].(int), |
| 163 | name: valid["name"].(string), |
| 164 | model: model, |
| 165 | path: valid["path"].(string), |
| 166 | usedFor: valid["used_for"].(string), |
| 167 | tags: convertToStringSlice(valid["tags"]), |
| 168 | |
| 169 | blockSize: valid["block_size"].(uint64), |
| 170 | usedSize: valid["used_size"].(uint64), |
| 171 | size: valid["size"].(uint64), |
| 172 | |
| 173 | partitions: partitions, |
| 174 | } |
| 175 | return result, nil |
| 176 | } |