func TestVolumeActions(t *testing.T) { client, err := newClient(t) th.AssertNoErr(t, err) cv, err := volumes.Create(client, &volumes.CreateOpts{ Size: 1, Name: "blockv2-volume", }).Extract() th.AssertNoErr(t, err) defer func() { err = volumes.WaitForStatus(client, cv.ID, "available", 60) th.AssertNoErr(t, err) err = volumes.Delete(client, cv.ID).ExtractErr() th.AssertNoErr(t, err) }() err = volumes.WaitForStatus(client, cv.ID, "available", 60) th.AssertNoErr(t, err) _, err = volumeactions.Attach(client, cv.ID, &volumeactions.AttachOpts{ MountPoint: "/mnt", Mode: "rw", InstanceUUID: "50902f4f-a974-46a0-85e9-7efc5e22dfdd", }).Extract() th.AssertNoErr(t, err) err = volumes.WaitForStatus(client, cv.ID, "in-use", 60) th.AssertNoErr(t, err) _, err = volumeactions.Detach(client, cv.ID).Extract() th.AssertNoErr(t, err) }
func (client *cinderClient) attach(id string, opts volumeactions.AttachOpts) error { attachResult := volumeactions.Attach(client.cinder, id, opts) if attachResult.Err != nil && attachResult.Err.Error() != "EOF" { return attachResult.Err } return nil }
func TestVolumeAttach(t *testing.T) { client, err := newClient(t) th.AssertNoErr(t, err) t.Logf("Creating volume") cv, err := volumes.Create(client, &volumes.CreateOpts{ Size: 1, Name: "blockv2-volume", }).Extract() th.AssertNoErr(t, err) defer func() { err = volumes.WaitForStatus(client, cv.ID, "available", 60) th.AssertNoErr(t, err) t.Logf("Deleting volume") err = volumes.Delete(client, cv.ID).ExtractErr() th.AssertNoErr(t, err) }() err = volumes.WaitForStatus(client, cv.ID, "available", 60) th.AssertNoErr(t, err) instanceID := os.Getenv("OS_INSTANCE_ID") if instanceID == "" { t.Fatal("Environment variable OS_INSTANCE_ID is required") } t.Logf("Attaching volume") err = volumeactions.Attach(client, cv.ID, &volumeactions.AttachOpts{ MountPoint: "/mnt", Mode: "rw", InstanceUUID: instanceID, }).ExtractErr() th.AssertNoErr(t, err) err = volumes.WaitForStatus(client, cv.ID, "in-use", 60) th.AssertNoErr(t, err) t.Logf("Detaching volume") err = volumeactions.Detach(client, cv.ID).ExtractErr() th.AssertNoErr(t, err) }
func (d CinderDriver) Mount(r volume.Request) volume.Response { d.Mutex.Lock() defer d.Mutex.Unlock() hostname, _ := os.Hostname() log.Infof("Mounting volume %+v on %s", r, hostname) vol, err := d.getByName(r.Name) if err != nil { log.Errorf("Failed to retrieve volume named: ", r.Name, "during Mount operation", err) return volume.Response{Err: err.Error()} } if vol.ID == "" { log.Error("Volume Not Found!") err := errors.New("Volume Not Found") return volume.Response{Err: err.Error()} } if vol.Status == "creating" { // NOTE(jdg): This may be a successive call after a create which from // the docker volume api can be quite speedy. Take a short pause and // check the status again before proceeding time.Sleep(time.Second * 5) vol, err = d.getByName(r.Name) } if err != nil { log.Errorf("Failed to retrieve volume named: ", r.Name, "during Mount operation", err) return volume.Response{Err: err.Error()} } if vol.Status != "available" { log.Debugf("Volume info: %+v\n", vol) log.Errorf("Invalid volume status for Mount request, volume is: %s but must be available", vol.Status) err := errors.New("Invalid volume status for Mount request") return volume.Response{Err: err.Error()} } volumeactions.Reserve(d.Client, vol.ID) iface := d.Conf.InitiatorIFace netDev, _ := net.InterfaceByName(iface) IPs, _ := net.InterfaceAddrs() log.Debugf("iface: %+v\n Addrs: %+v", netDev, IPs) log.Debug("Gather up initiator IQNs...") initiator, err := GetInitiatorIqns() if err != nil { log.Error("Failed to retrieve Initiator name!") return volume.Response{Err: err.Error()} } // TODO(ebalduf): Change assumption that we have only one Initiator defined log.Debugf("Value of IPs is=%+v\n", IPs) connectorOpts := volumeactions.ConnectorOpts{ IP: d.Conf.InitiatorIP, Host: hostname, Initiator: initiator[0], Wwpns: []string{}, Wwnns: "", Multipath: false, Platform: "x86", OSType: "linux", } log.Debug("Issue InitializeConnection...") response := volumeactions.InitializeConnection(d.Client, vol.ID, &connectorOpts) log.Debugf("Response from InitializeConnection: %+v\n", response) data := response.Body.(map[string]interface{})["connection_info"].(map[string]interface{})["data"] var con ConnectorInfo mapstructure.Decode(data, &con) path, device, err := attachVolume(&con, "default") log.Debug("iSCSI connection done") if path == "" || device == "" && err == nil { log.Error("Missing path or device, but err not set?") log.Debug("Path: ", path, " ,Device: ", device) return volume.Response{Err: err.Error()} } if err != nil { log.Errorf("Failed to perform iscsi attach of volume %s: %v", r.Name, err) return volume.Response{Err: err.Error()} } if GetFSType(device) == "" { //TODO(jdg): Enable selection of *other* fs types log.Debugf("Formatting device") err := FormatVolume(device, "ext4") if err != nil { err := errors.New("Failed to format device") log.Error(err) return volume.Response{Err: err.Error()} } } if mountErr := Mount(device, d.Conf.MountPoint+"/"+r.Name); mountErr != nil { err := errors.New("Problem mounting docker volume ") log.Error(err) return volume.Response{Err: err.Error()} } path = filepath.Join(d.Conf.MountPoint, r.Name) // NOTE(jdg): Cinder will barf if you provide both Instance and HostName // which is kinda silly... but it is what it is attachOpts := volumeactions.AttachOpts{ MountPoint: path, InstanceUUID: d.Conf.HostUUID, HostName: "", Mode: "rw"} log.Debug("Call gophercloud Attach...") attRes := volumeactions.Attach(d.Client, vol.ID, &attachOpts) log.Debugf("Attach results: %+v", attRes) return volume.Response{Mountpoint: d.Conf.MountPoint + "/" + r.Name} }