Skip to content

Commit

Permalink
Merge pull request #135 from wallyworld/revise-blockdevices
Browse files Browse the repository at this point in the history
#135

Twp changes to block device handling:
- add missing serial id attribute to block devices
- add new model method to add a block device to a specified machine

The block device entity has missing a serial id attribute which was added to juju and not updated here.

With the move of juju to dqlite, exporting a model will result in all block devices being loaded separately to machines. So we need a new helper method on model to allow block devices to be added for a specific machine id, rather than being part of the machine entity itself.
  • Loading branch information
jujubot authored Jan 19, 2024
2 parents fe370df + 2280efc commit 9de8d67
Show file tree
Hide file tree
Showing 5 changed files with 114 additions and 6 deletions.
37 changes: 36 additions & 1 deletion blockdevice.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ type BlockDevice interface {
Label() string
UUID() string
HardwareID() string
SerialID() string
WWN() string
BusAddress() string
Size() uint64
Expand All @@ -40,6 +41,7 @@ type blockdevice struct {
Label_ string `yaml:"label,omitempty"`
UUID_ string `yaml:"uuid,omitempty"`
HardwareID_ string `yaml:"hardware-id,omitempty"`
SerialID_ string `yaml:"serial-id,omitempty"`
WWN_ string `yaml:"wwn,omitempty"`
BusAddress_ string `yaml:"bus-address,omitempty"`
Size_ uint64 `yaml:"size"`
Expand All @@ -57,6 +59,7 @@ type BlockDeviceArgs struct {
HardwareID string
WWN string
BusAddress string
SerialID string
Size uint64
FilesystemType string
InUse bool
Expand All @@ -70,6 +73,7 @@ func newBlockDevice(args BlockDeviceArgs) *blockdevice {
Label_: args.Label,
UUID_: args.UUID,
HardwareID_: args.HardwareID,
SerialID_: args.SerialID,
WWN_: args.WWN,
BusAddress_: args.BusAddress,
Size_: args.Size,
Expand Down Expand Up @@ -106,6 +110,11 @@ func (b *blockdevice) HardwareID() string {
return b.HardwareID_
}

// SerialID implements BlockDevice.
func (b *blockdevice) SerialID() string {
return b.SerialID_
}

// WWN implements BlockDevice.
func (b *blockdevice) WWN() string {
return b.WWN_
Expand Down Expand Up @@ -173,9 +182,10 @@ type blockdeviceDeserializationFunc func(map[string]interface{}) (*blockdevice,

var blockdeviceDeserializationFuncs = map[int]blockdeviceDeserializationFunc{
1: importBlockDeviceV1,
2: importBlockDeviceV2,
}

func importBlockDeviceV1(source map[string]interface{}) (*blockdevice, error) {
func blockDeviceV1Fields() (schema.Fields, schema.Defaults) {
fields := schema.Fields{
"name": schema.String(),
"links": schema.List(schema.String()),
Expand All @@ -200,6 +210,27 @@ func importBlockDeviceV1(source map[string]interface{}) (*blockdevice, error) {
"fs-type": "",
"mount-point": "",
}
return fields, defaults
}

func blockDeviceV2Fields() (schema.Fields, schema.Defaults) {
fields, defaults := blockDeviceV1Fields()
fields["serial-id"] = schema.String()
defaults["serial-id"] = ""
return fields, defaults
}

func importBlockDeviceV1(source map[string]interface{}) (*blockdevice, error) {
fields, defaults := blockDeviceV1Fields()
return importBlockDevice(fields, defaults, 1, source)
}

func importBlockDeviceV2(source map[string]interface{}) (*blockdevice, error) {
fields, defaults := blockDeviceV2Fields()
return importBlockDevice(fields, defaults, 2, source)
}

func importBlockDevice(fields schema.Fields, defaults schema.Defaults, importVersion int, source map[string]interface{}) (*blockdevice, error) {
checker := schema.FieldMap(fields, defaults)

coerced, err := checker.Coerce(source, nil)
Expand All @@ -223,5 +254,9 @@ func importBlockDeviceV1(source map[string]interface{}) (*blockdevice, error) {
MountPoint_: valid["mount-point"].(string),
}

if importVersion >= 2 {
result.SerialID_ = valid["serial-id"].(string)
}

return result, nil
}
21 changes: 17 additions & 4 deletions blockdevice_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ func allBlockDeviceArgs() BlockDeviceArgs {
Label: "sda",
UUID: "some-uuid",
HardwareID: "magic",
SerialID: "coco-pops",
WWN: "drbr",
BusAddress: "bus stop",
Size: 16 * 1024 * 1024 * 1024,
Expand All @@ -50,6 +51,7 @@ func (s *BlockDeviceSerializationSuite) TestNewBlockDevice(c *gc.C) {
c.Check(d.Label(), gc.Equals, "sda")
c.Check(d.UUID(), gc.Equals, "some-uuid")
c.Check(d.HardwareID(), gc.Equals, "magic")
c.Check(d.SerialID(), gc.Equals, "coco-pops")
c.Check(d.WWN(), gc.Equals, "drbr")
c.Check(d.BusAddress(), gc.Equals, "bus stop")
c.Check(d.Size(), gc.Equals, uint64(16*1024*1024*1024))
Expand All @@ -58,9 +60,9 @@ func (s *BlockDeviceSerializationSuite) TestNewBlockDevice(c *gc.C) {
c.Check(d.MountPoint(), gc.Equals, "/")
}

func (s *BlockDeviceSerializationSuite) exportImport(c *gc.C, dev *blockdevice) *blockdevice {
func (s *BlockDeviceSerializationSuite) exportImport(c *gc.C, dev *blockdevice, version int) *blockdevice {
initial := blockdevices{
Version: 1,
Version: version,
BlockDevices_: []*blockdevice{dev},
}

Expand All @@ -77,9 +79,20 @@ func (s *BlockDeviceSerializationSuite) exportImport(c *gc.C, dev *blockdevice)
return devices[0]
}

func (s *BlockDeviceSerializationSuite) exportImportLatest(c *gc.C, dev *blockdevice) *blockdevice {
return s.exportImport(c, dev, 2)
}

func (s *BlockDeviceSerializationSuite) TestParsingSerializedData(c *gc.C) {
initial := newBlockDevice(allBlockDeviceArgs())
imported := s.exportImport(c, initial)
imported := s.exportImportLatest(c, initial)
c.Assert(imported, jc.DeepEquals, initial)
}

func (s *BlockDeviceSerializationSuite) TestV1ParsingReturnsLatest(c *gc.C) {
initial := newBlockDevice(allBlockDeviceArgs())
imported := s.exportImport(c, initial, 1)
initial.SerialID_ = ""
c.Assert(imported, jc.DeepEquals, initial)
}

Expand All @@ -91,7 +104,7 @@ func (s *BlockDeviceSerializationSuite) TestImportEmpty(c *gc.C) {

func emptyBlockDeviceMap() map[interface{}]interface{} {
return map[interface{}]interface{}{
"version": 1,
"version": 2,
"block-devices": []interface{}{},
}
}
2 changes: 1 addition & 1 deletion machine.go
Original file line number Diff line number Diff line change
Expand Up @@ -318,7 +318,7 @@ func (m *machine) AddBlockDevice(args BlockDeviceArgs) BlockDevice {

func (m *machine) setBlockDevices(devices []*blockdevice) {
m.BlockDevices_ = blockdevices{
Version: 1,
Version: 2,
BlockDevices_: devices,
}
}
Expand Down
15 changes: 15 additions & 0 deletions model.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
package description

import (
"fmt"
"net"
"sort"
"strings"
Expand Down Expand Up @@ -136,6 +137,8 @@ type Model interface {
MeterStatus() MeterStatus

PasswordHash() string

AddBlockDevice(string, BlockDeviceArgs) error
}

// ModelArgs represent the bare minimum information that is needed
Expand Down Expand Up @@ -431,6 +434,18 @@ func (m *model) setMachines(machineList []*machine) {
}
}

// AddBlockDevice adds a block device for the specified machine.
func (m *model) AddBlockDevice(machineId string, bdArgs BlockDeviceArgs) error {
for i := range m.Machines_.Machines_ {
if m.Machines_.Machines_[i].Id_ != machineId {
continue
}
m.Machines_.Machines_[i].AddBlockDevice(bdArgs)
return nil
}
return fmt.Errorf("machine %q %w", machineId, errors.NotFound)
}

// Applications implements Model.
func (m *model) Applications() []Application {
var result []Application
Expand Down
45 changes: 45 additions & 0 deletions model_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -324,6 +324,51 @@ func (s *ModelSerializationSuite) addMachineToModel(model Model, id string) Mach
return machine
}

func (s *ModelSerializationSuite) TestAddBlockDevices(c *gc.C) {
model := s.newModel(ModelArgs{Owner: names.NewUserTag("owner"), CloudRegion: "some-region"})
model.AddMachine(MachineArgs{Id: names.NewMachineTag("666")})
err := model.AddBlockDevice("666", BlockDeviceArgs{
Name: "foo",
Links: []string{"a-link"},
Label: "label",
UUID: "device-uuid",
HardwareID: "hardware-id",
WWN: "wwn",
BusAddress: "bus-address",
SerialID: "serial-id",
Size: 100,
FilesystemType: "ext4",
InUse: true,
MountPoint: "/path/to/here",
})
c.Assert(err, jc.ErrorIsNil)
m := model.Machines()
c.Assert(m, gc.HasLen, 1)
blockDevices := m[0].BlockDevices()
c.Assert(blockDevices, gc.HasLen, 1)
bd := blockDevices[0]
c.Assert(bd.Name(), gc.Equals, "foo")
c.Assert(bd.Links(), jc.DeepEquals, []string{"a-link"})
c.Assert(bd.Label(), gc.Equals, "label")
c.Assert(bd.UUID(), gc.Equals, "device-uuid")
c.Assert(bd.HardwareID(), gc.Equals, "hardware-id")
c.Assert(bd.WWN(), gc.Equals, "wwn")
c.Assert(bd.BusAddress(), gc.Equals, "bus-address")
c.Assert(bd.SerialID(), gc.Equals, "serial-id")
c.Assert(bd.Size(), gc.Equals, uint64(100))
c.Assert(bd.FilesystemType(), gc.Equals, "ext4")
c.Assert(bd.InUse(), jc.IsTrue)
c.Assert(bd.MountPoint(), gc.Equals, "/path/to/here")
}

func (s *ModelSerializationSuite) TestAddBlockDevicesMachineNotFound(c *gc.C) {
model := s.newModel(ModelArgs{Owner: names.NewUserTag("owner"), CloudRegion: "some-region"})
err := model.AddBlockDevice("666", BlockDeviceArgs{
Name: "foo",
})
c.Assert(err, jc.ErrorIs, errors.NotFound)
}

func (s *ModelSerializationSuite) TestModelValidationChecksMachinesGood(c *gc.C) {
model := s.newModel(ModelArgs{Owner: names.NewUserTag("owner"), CloudRegion: "some-region"})
s.addMachineToModel(model, "0")
Expand Down

0 comments on commit 9de8d67

Please sign in to comment.