diff --git a/ci/scripts/upgrade_ndm.sh b/ci/scripts/upgrade_ndm.sh index efa0620a..83db8bea 100755 --- a/ci/scripts/upgrade_ndm.sh +++ b/ci/scripts/upgrade_ndm.sh @@ -60,7 +60,7 @@ sleep 10 # wait 10 seconds for ndm start to respwan pods wait_ndm_ready # check image -pod_name=$(kubectl get pods -n harvester-system |grep ^harvester-node-disk-manager|head -n1 |awk '{print $1}') +pod_name=$(kubectl get pods -n harvester-system |grep Running |grep ^harvester-node-disk-manager|head -n1 |awk '{print $1}') container_img=$(kubectl get pods ${pod_name} -n harvester-system -o yaml |yq -e .spec.containers[0].image |tr ":" \n) yaml_img=$(yq -e .image.repository ndm-override.yaml) if grep -q ${yaml_img} <<< ${container_img}; then diff --git a/pkg/utils/virt.go b/pkg/utils/virt.go new file mode 100644 index 00000000..26489404 --- /dev/null +++ b/pkg/utils/virt.go @@ -0,0 +1,68 @@ +package utils + +import ( + "encoding/xml" + "fmt" + "os" +) + +type Disk struct { + XMLName xml.Name `xml:"disk"` + Type string `xml:"type,attr"` + Device string `xml:"device,attr"` + Driver Driver `xml:"driver"` + Source Source `xml:"source"` + Target Target `xml:"target"` + WWN string `xml:"wwn"` +} + +type Driver struct { + Name string `xml:"name,attr"` + Type string `xml:"type,attr"` +} + +type Source struct { + File string `xml:"file,attr"` +} + +type Target struct { + Dev string `xml:"dev,attr"` + Bus string `xml:"bus,attr"` +} + +// DiskXMLReader can read the libvirt disk xml file and return a Disk struct +func DiskXMLReader(filePath string) (Disk, error) { + f, err := os.Open(filePath) + if err != nil { + return Disk{}, fmt.Errorf("open file(%s) error: %v", filePath, err) + } + + defer f.Close() + + var disk Disk + err = xml.NewDecoder(f).Decode(&disk) + if err != nil { + return Disk{}, fmt.Errorf("decode XML Error: %v", err) + } + + return disk, nil +} + +// XMLWriter write XML to target file, make sure your xmlData should valid +func XMLWriter(targetFilePath string, xmlData any) error { + // Create a new file for writing + targetFile, err := os.Create(targetFilePath) + if err != nil { + return fmt.Errorf("create file(%s) error: %v", targetFilePath, err) + } + defer targetFile.Close() + + // Encode the disk data and write it to the output file + encoder := xml.NewEncoder(targetFile) + err = encoder.Encode(xmlData) + if err != nil { + return fmt.Errorf("encod XML Error: %v", err) + } + + return nil +} diff --git a/tests/integration/test_1_disk_hotplug_test.go b/tests/integration/test_1_disk_hotplug_test.go index 7e4e4bb6..555a1099 100644 --- a/tests/integration/test_1_disk_hotplug_test.go +++ b/tests/integration/test_1_disk_hotplug_test.go @@ -20,6 +20,7 @@ import ( diskv1 "github.com/harvester/node-disk-manager/pkg/apis/harvesterhci.io/v1beta1" clientset "github.com/harvester/node-disk-manager/pkg/generated/clientset/versioned" + "github.com/harvester/node-disk-manager/pkg/utils" ) /* @@ -44,6 +45,7 @@ type HotPlugTestSuite struct { clientSet *clientset.Clientset targetNodeName string targetDiskName string + curBusPath string // to make sure which path we deployed } func (s *HotPlugTestSuite) SetupSuite() { @@ -148,6 +150,43 @@ func (s *HotPlugTestSuite) Test_2_HotPlugAddDisk() { require.Equal(s.T(), err, nil, "Get Blockdevices should not get error") require.Equal(s.T(), curBlockdevice.Status.State, diskv1.BlockDeviceActive, "Disk status should be inactive after we add disk") + s.curBusPath = curBlockdevice.Status.DeviceStatus.Details.BusPath +} + +func (s *HotPlugTestSuite) Test_3_AddDuplicatedWWNDsik() { + // create another another disk raw file and xml + const ( + originalDeviceRaw = "/tmp/hotplug_disks/node1-sda.qcow2" + duplicatedDeviceXML = "/tmp/hotplug_disks/node1-sdb.xml" + duplicatedDeviceRaw = "/tmp/hotplug_disks/node1-sdb.qcow2" + ) + cmdCpyRawFile := fmt.Sprintf("cp %s %s", originalDeviceRaw, duplicatedDeviceRaw) + _, _, err := doCommand(cmdCpyRawFile) + require.Equal(s.T(), err, nil, "Running command `cp the raw device file` should not get error") + + disk, err := utils.DiskXMLReader(hotplugDiskXMLFileName) + require.Equal(s.T(), err, nil, "Read xml file should not get error") + disk.Source.File = duplicatedDeviceRaw + disk.Target.Dev = "sdb" + err = utils.XMLWriter(duplicatedDeviceXML, disk) + require.Equal(s.T(), err, nil, "Write xml file should not get error") + + cmd := fmt.Sprintf("virsh attach-device --domain %s --file %s --live", hotplugTargetNodeName, duplicatedDeviceXML) + _, _, err = doCommand(cmd) + require.Equal(s.T(), err, nil, "Running command `virsh attach-device` should not get error") + + // wait for controller handling + time.Sleep(5 * time.Second) + + // check disk status + require.NotEqual(s.T(), s.targetDiskName, "", "target disk name should not be empty before we start hotplug (add) test") + bdi := s.clientSet.HarvesterhciV1beta1().BlockDevices("longhorn-system") + blockdeviceList, err := bdi.List(context.TODO(), v1.ListOptions{}) + require.Equal(s.T(), err, nil, "Get BlockdevicesList should not get error") + require.Equal(s.T(), 1, len(blockdeviceList.Items), "We should have one disks because duplicated wwn should not added") + curBlockdevice, err := bdi.Get(context.TODO(), s.targetDiskName, v1.GetOptions{}) + require.Equal(s.T(), err, nil, "Get Blockdevices should not get error") + require.Equal(s.T(), s.curBusPath, curBlockdevice.Status.DeviceStatus.Details.BusPath, "Disk path should not replace by duplicated wwn disk") }