Beispiel #1
0
func (s *cinderVolumeSourceSuite) TestDestroyVolumesAttached(c *gc.C) {
	statuses := []string{"in-use", "detaching", "available"}

	mockAdapter := &mockAdapter{
		getVolume: func(volId string) (*cinder.Volume, error) {
			c.Assert(statuses, gc.Not(gc.HasLen), 0)
			status := statuses[0]
			statuses = statuses[1:]
			return &cinder.Volume{
				ID:     volId,
				Status: status,
			}, nil
		},
	}

	volSource := openstack.NewCinderVolumeSource(mockAdapter)
	errs, err := volSource.DestroyVolumes([]string{mockVolId})
	c.Assert(err, jc.ErrorIsNil)
	c.Assert(errs, gc.HasLen, 1)
	c.Assert(errs[0], jc.ErrorIsNil)
	c.Assert(statuses, gc.HasLen, 0)
	mockAdapter.CheckCalls(c, []gitjujutesting.StubCall{{
		"GetVolume", []interface{}{mockVolId},
	}, {
		"GetVolume", []interface{}{mockVolId},
	}, {
		"GetVolume", []interface{}{mockVolId},
	}, {
		"DeleteVolume", []interface{}{mockVolId},
	}})
}
Beispiel #2
0
func (s *cinderVolumeSourceSuite) TestAttachVolumes(c *gc.C) {
	mockAdapter := &mockAdapter{
		attachVolume: func(serverId, volId, mountPoint string) (*nova.VolumeAttachment, error) {
			c.Check(volId, gc.Equals, mockVolId)
			c.Check(serverId, gc.Equals, mockServerId)
			return &nova.VolumeAttachment{
				Id:       volId,
				VolumeId: volId,
				ServerId: serverId,
				Device:   "/dev/sda",
			}, nil
		},
	}

	volSource := openstack.NewCinderVolumeSource(mockAdapter)
	results, err := volSource.AttachVolumes([]storage.VolumeAttachmentParams{{
		Volume:   mockVolumeTag,
		VolumeId: mockVolId,
		AttachmentParams: storage.AttachmentParams{
			Provider:   openstack.CinderProviderType,
			Machine:    mockMachineTag,
			InstanceId: instance.Id(mockServerId),
		}},
	})
	c.Assert(err, jc.ErrorIsNil)
	c.Check(results, jc.DeepEquals, []storage.AttachVolumesResult{{
		VolumeAttachment: &storage.VolumeAttachment{
			mockVolumeTag,
			mockMachineTag,
			storage.VolumeAttachmentInfo{
				DeviceName: "sda",
			},
		},
	}})
}
Beispiel #3
0
func (s *cinderVolumeSourceSuite) TestCreateVolumeFails(c *gc.C) {
	volSource := openstack.NewCinderVolumeSource(&mockAdapter{})
	_, _, err := volSource.CreateVolumes([]storage.VolumeParams{{
		Provider:   openstack.CinderProviderType,
		Attributes: map[string]interface{}{storage.Persistent: false},
	}})
	c.Assert(err, gc.ErrorMatches, "cannot create a non-persistent Cinder volume")
}
Beispiel #4
0
func (s *cinderVolumeSourceSuite) TestDestroyVolumes(c *gc.C) {
	mockAdapter := &mockAdapter{}
	volSource := openstack.NewCinderVolumeSource(mockAdapter)
	errs := volSource.DestroyVolumes([]string{mockVolId})
	c.Assert(errs, jc.DeepEquals, []error{nil})
	mockAdapter.CheckCalls(c, []gitjujutesting.StubCall{
		{"GetVolume", []interface{}{mockVolId}},
		{"DeleteVolume", []interface{}{mockVolId}},
	})
}
Beispiel #5
0
func (s *cinderVolumeSourceSuite) TestDetachVolumes(c *gc.C) {
	const mockServerId2 = mockServerId + "2"

	var numListCalls, numDetachCalls int
	mockAdapter := &mockAdapter{
		listVolumeAttachments: func(serverId string) ([]nova.VolumeAttachment, error) {
			numListCalls++
			if serverId == mockServerId2 {
				// no attachments
				return nil, nil
			}
			c.Check(serverId, gc.Equals, mockServerId)
			return []nova.VolumeAttachment{{
				Id:       mockVolId,
				VolumeId: mockVolId,
				ServerId: mockServerId,
				Device:   "/dev/sda",
			}}, nil
		},
		detachVolume: func(serverId, volId string) error {
			numDetachCalls++
			c.Check(serverId, gc.Equals, mockServerId)
			c.Check(volId, gc.Equals, mockVolId)
			return nil
		},
	}

	volSource := openstack.NewCinderVolumeSource(mockAdapter)
	errs, err := volSource.DetachVolumes([]storage.VolumeAttachmentParams{{
		Volume:   names.NewVolumeTag("123"),
		VolumeId: mockVolId,
		AttachmentParams: storage.AttachmentParams{
			Machine:    names.NewMachineTag("0"),
			InstanceId: mockServerId,
		},
	}, {
		Volume:   names.NewVolumeTag("42"),
		VolumeId: "42",
		AttachmentParams: storage.AttachmentParams{
			Machine:    names.NewMachineTag("0"),
			InstanceId: mockServerId2,
		},
	}})
	c.Assert(err, jc.ErrorIsNil)
	c.Assert(errs, jc.DeepEquals, []error{nil, nil})
	// DetachVolume should only be called for existing attachments.
	mockAdapter.CheckCalls(c, []gitjujutesting.StubCall{{
		"ListVolumeAttachments", []interface{}{mockServerId},
	}, {
		"DetachVolume", []interface{}{mockServerId, mockVolId},
	}, {
		"ListVolumeAttachments", []interface{}{mockServerId2},
	}})
}
Beispiel #6
0
func (s *cinderVolumeSourceSuite) TestResourceTags(c *gc.C) {
	var created bool
	mockAdapter := &mockAdapter{
		createVolume: func(args cinder.CreateVolumeVolumeParams) (*cinder.Volume, error) {
			created = true
			c.Assert(args, jc.DeepEquals, cinder.CreateVolumeVolumeParams{
				Size: 1,
				Name: "juju-testenv-volume-123",
				Metadata: map[string]string{
					"ResourceTag1": "Value1",
					"ResourceTag2": "Value2",
				},
			})
			return &cinder.Volume{ID: mockVolId}, nil
		},
		getVolume: func(volumeId string) (*cinder.Volume, error) {
			return &cinder.Volume{
				ID:     volumeId,
				Size:   1,
				Status: "available",
			}, nil
		},
		attachVolume: func(serverId, volId, mountPoint string) (*nova.VolumeAttachment, error) {
			return &nova.VolumeAttachment{
				Id:       volId,
				VolumeId: volId,
				ServerId: serverId,
				Device:   "/dev/sda",
			}, nil
		},
	}

	volSource := openstack.NewCinderVolumeSource(mockAdapter)
	_, err := volSource.CreateVolumes([]storage.VolumeParams{{
		Provider: openstack.CinderProviderType,
		Tag:      mockVolumeTag,
		Size:     1024,
		ResourceTags: map[string]string{
			"ResourceTag1": "Value1",
			"ResourceTag2": "Value2",
		},
		Attachment: &storage.VolumeAttachmentParams{
			AttachmentParams: storage.AttachmentParams{
				Provider:   openstack.CinderProviderType,
				Machine:    mockMachineTag,
				InstanceId: instance.Id(mockServerId),
			},
		},
	}})
	c.Assert(err, jc.ErrorIsNil)
	c.Assert(created, jc.IsTrue)
}
Beispiel #7
0
func (s *cinderVolumeSourceSuite) TestDestroyVolumes(c *gc.C) {
	var numCalls int
	mockAdapter := &mockAdapter{
		deleteVolume: func(volId string) error {
			numCalls++
			c.Check(volId, gc.Equals, mockVolId)
			return nil
		},
	}

	volSource := openstack.NewCinderVolumeSource(mockAdapter)
	errs := volSource.DestroyVolumes([]string{mockVolId})
	c.Assert(numCalls, gc.Equals, 1)
	c.Assert(errs, jc.DeepEquals, []error{nil})
}
Beispiel #8
0
func (s *cinderVolumeSourceSuite) TestDescribeVolumes(c *gc.C) {
	mockAdapter := &mockAdapter{
		getVolumesSimple: func() ([]cinder.Volume, error) {
			return []cinder.Volume{{
				ID:   mockVolId,
				Size: mockVolSize / 1024,
			}}, nil
		},
	}
	volSource := openstack.NewCinderVolumeSource(mockAdapter)
	volumes, err := volSource.DescribeVolumes([]string{mockVolId})
	c.Assert(err, jc.ErrorIsNil)
	c.Check(volumes, jc.DeepEquals, []storage.VolumeInfo{{
		VolumeId:   mockVolId,
		Size:       mockVolSize,
		Persistent: true,
	}})
}
Beispiel #9
0
func (s *cinderVolumeSourceSuite) TestListVolumes(c *gc.C) {
	mockAdapter := &mockAdapter{
		getVolumesDetail: func() ([]cinder.Volume, error) {
			return []cinder.Volume{{
				ID: "volume-1",
			}, {
				ID: "volume-2",
				Metadata: map[string]string{
					tags.JujuEnv: "something-else",
				},
			}, {
				ID: "volume-3",
				Metadata: map[string]string{
					tags.JujuEnv: testing.EnvironmentTag.Id(),
				},
			}}, nil
		},
	}
	volSource := openstack.NewCinderVolumeSource(mockAdapter)
	volumeIds, err := volSource.ListVolumes()
	c.Assert(err, jc.ErrorIsNil)
	c.Check(volumeIds, jc.DeepEquals, []string{"volume-3"})
}
Beispiel #10
0
func (s *cinderVolumeSourceSuite) TestCreateVolume(c *gc.C) {
	const (
		requestedSize = 2 * 1024
		providedSize  = 3 * 1024
	)

	s.PatchValue(openstack.CinderAttempt, utils.AttemptStrategy{Min: 3})

	var getVolumeCalls int
	mockAdapter := &mockAdapter{
		createVolume: func(args cinder.CreateVolumeVolumeParams) (*cinder.Volume, error) {
			c.Assert(args, jc.DeepEquals, cinder.CreateVolumeVolumeParams{
				Size: requestedSize / 1024,
				Name: "juju-testenv-volume-123",
			})
			return &cinder.Volume{
				ID: mockVolId,
			}, nil
		},
		getVolume: func(volumeId string) (*cinder.Volume, error) {
			var status string
			getVolumeCalls++
			if getVolumeCalls > 1 {
				status = "available"
			}
			return &cinder.Volume{
				ID:     volumeId,
				Size:   providedSize / 1024,
				Status: status,
			}, nil
		},
		attachVolume: func(serverId, volId, mountPoint string) (*nova.VolumeAttachment, error) {
			c.Check(volId, gc.Equals, mockVolId)
			c.Check(serverId, gc.Equals, mockServerId)
			return &nova.VolumeAttachment{
				Id:       volId,
				VolumeId: volId,
				ServerId: serverId,
				Device:   "/dev/sda",
			}, nil
		},
	}

	volSource := openstack.NewCinderVolumeSource(mockAdapter)
	results, err := volSource.CreateVolumes([]storage.VolumeParams{{
		Provider: openstack.CinderProviderType,
		Tag:      mockVolumeTag,
		Size:     requestedSize,
		Attachment: &storage.VolumeAttachmentParams{
			AttachmentParams: storage.AttachmentParams{
				Provider:   openstack.CinderProviderType,
				Machine:    mockMachineTag,
				InstanceId: instance.Id(mockServerId),
			},
		},
	}})
	c.Assert(err, jc.ErrorIsNil)
	c.Assert(results, gc.HasLen, 1)
	c.Assert(results[0].Error, jc.ErrorIsNil)

	c.Check(results[0].Volume, jc.DeepEquals, &storage.Volume{
		mockVolumeTag,
		storage.VolumeInfo{
			VolumeId:   mockVolId,
			Size:       providedSize,
			Persistent: true,
		},
	})

	// should have been 2 calls to GetVolume: twice initially
	// to wait until the volume became available.
	c.Check(getVolumeCalls, gc.Equals, 2)
}
Beispiel #11
0
func (s *cinderVolumeSourceSuite) TestCreateVolumeCleanupDestroys(c *gc.C) {
	var numCreateCalls, numDestroyCalls, numGetCalls int
	mockAdapter := &mockAdapter{
		createVolume: func(args cinder.CreateVolumeVolumeParams) (*cinder.Volume, error) {
			numCreateCalls++
			if numCreateCalls == 3 {
				return nil, errors.New("no volume for you")
			}
			return &cinder.Volume{
				ID:     fmt.Sprint(numCreateCalls),
				Status: "",
			}, nil
		},
		deleteVolume: func(volId string) error {
			numDestroyCalls++
			c.Assert(volId, gc.Equals, "2")
			return errors.New("destroy fails")
		},
		getVolume: func(volumeId string) (*cinder.Volume, error) {
			numGetCalls++
			if numGetCalls == 2 {
				return nil, errors.New("no volume details for you")
			}
			return &cinder.Volume{
				ID:     "4",
				Size:   mockVolSize / 1024,
				Status: "available",
			}, nil
		},
	}

	volSource := openstack.NewCinderVolumeSource(mockAdapter)
	volumeParams := []storage.VolumeParams{{
		Provider: openstack.CinderProviderType,
		Tag:      names.NewVolumeTag("0"),
		Size:     mockVolSize,
		Attachment: &storage.VolumeAttachmentParams{
			AttachmentParams: storage.AttachmentParams{
				Provider:   openstack.CinderProviderType,
				Machine:    mockMachineTag,
				InstanceId: instance.Id(mockServerId),
			},
		},
	}, {
		Provider: openstack.CinderProviderType,
		Tag:      names.NewVolumeTag("1"),
		Size:     mockVolSize,
		Attachment: &storage.VolumeAttachmentParams{
			AttachmentParams: storage.AttachmentParams{
				Provider:   openstack.CinderProviderType,
				Machine:    mockMachineTag,
				InstanceId: instance.Id(mockServerId),
			},
		},
	}, {
		Provider: openstack.CinderProviderType,
		Tag:      names.NewVolumeTag("2"),
		Size:     mockVolSize,
		Attachment: &storage.VolumeAttachmentParams{
			AttachmentParams: storage.AttachmentParams{
				Provider:   openstack.CinderProviderType,
				Machine:    mockMachineTag,
				InstanceId: instance.Id(mockServerId),
			},
		},
	}}
	results, err := volSource.CreateVolumes(volumeParams)
	c.Assert(err, jc.ErrorIsNil)
	c.Assert(results, gc.HasLen, 3)
	c.Assert(results[0].Error, jc.ErrorIsNil)
	c.Assert(results[1].Error, gc.ErrorMatches, "waiting for volume to be provisioned: getting volume: no volume details for you")
	c.Assert(results[2].Error, gc.ErrorMatches, "no volume for you")
	c.Assert(numCreateCalls, gc.Equals, 3)
	c.Assert(numGetCalls, gc.Equals, 2)
	c.Assert(numDestroyCalls, gc.Equals, 1)
}
Beispiel #12
0
func (s *cinderVolumeSourceSuite) TestCreateVolumeCleanupDestroys(c *gc.C) {
	var numCreateCalls, numAttachCalls, numDetachCalls, numDestroyCalls int
	mockAdapter := &mockAdapter{
		createVolume: func(args cinder.CreateVolumeVolumeParams) (*cinder.Volume, error) {
			numCreateCalls++
			if numCreateCalls == 3 {
				return nil, errors.New("no volume for you")
			}
			return &cinder.Volume{
				ID:   fmt.Sprint(numCreateCalls),
				Size: mockVolSize / 1024,
			}, nil
		},
		attachVolume: func(serverId, volId, mountPoint string) (*nova.VolumeAttachment, error) {
			numAttachCalls++
			if numAttachCalls == 2 {
				return nil, errors.New("no attach for you")
			}
			return &nova.VolumeAttachment{
				Id:       volId,
				VolumeId: volId,
				ServerId: serverId,
				Device:   "/dev/sda" + volId,
			}, nil
		},
		detachVolume: func(serverId, volId string) error {
			numDetachCalls++
			return errors.New("detach fails")
		},
		deleteVolume: func(volId string) error {
			numDestroyCalls++
			return errors.New("destroy fails")
		},
		listVolumeAttachments: func(serverId string) ([]nova.VolumeAttachment, error) {
			return []nova.VolumeAttachment{{
				Id:       "4",
				VolumeId: "4",
				ServerId: serverId,
				Device:   "/dev/sda",
			}}, nil
		},
	}

	volSource := openstack.NewCinderVolumeSource(mockAdapter)
	volumeParams := []storage.VolumeParams{{
		Provider: openstack.CinderProviderType,
		Tag:      names.NewVolumeTag("0"),
		Size:     mockVolSize,
		Attachment: &storage.VolumeAttachmentParams{
			AttachmentParams: storage.AttachmentParams{
				Provider:   openstack.CinderProviderType,
				Machine:    mockMachineTag,
				InstanceId: instance.Id(mockServerId),
			},
		},
	}, {
		Provider: openstack.CinderProviderType,
		Tag:      names.NewVolumeTag("1"),
		Size:     mockVolSize,
		Attachment: &storage.VolumeAttachmentParams{
			AttachmentParams: storage.AttachmentParams{
				Provider:   openstack.CinderProviderType,
				Machine:    mockMachineTag,
				InstanceId: instance.Id(mockServerId),
			},
		},
	}, {
		Provider: openstack.CinderProviderType,
		Tag:      names.NewVolumeTag("2"),
		Size:     mockVolSize,
		Attachment: &storage.VolumeAttachmentParams{
			AttachmentParams: storage.AttachmentParams{
				Provider:   openstack.CinderProviderType,
				Machine:    mockMachineTag,
				InstanceId: instance.Id(mockServerId),
			},
		},
	}}
	volumes, attachments, err := volSource.CreateVolumes(volumeParams)
	c.Assert(err, gc.ErrorMatches, "no volume for you")
	c.Assert(volumes, gc.IsNil)
	c.Assert(attachments, gc.IsNil)
	c.Assert(numCreateCalls, gc.Equals, 3)
	c.Assert(numDestroyCalls, gc.Equals, 2)

	// Second time around, the create calls should all succeed
	// but the second attach should fail. This will cause the
	// volumes to be detached and destroyed. One of the detachments
	// fails, so we should only see two destroy calls.
	_, _, err = volSource.CreateVolumes(volumeParams)
	c.Assert(err, gc.ErrorMatches, "no attach for you")
	c.Assert(numCreateCalls, gc.Equals, 6)
	c.Assert(numAttachCalls, gc.Equals, 2)
	c.Assert(numDetachCalls, gc.Equals, 1)
	c.Assert(numDestroyCalls, gc.Equals, 4)
}