Blame vendor/github.com/vmware/govmomi/object/datastore_file_manager.go

Packit 63bb0d
/*
Packit 63bb0d
Copyright (c) 2017-2018 VMware, Inc. All Rights Reserved.
Packit 63bb0d
Packit 63bb0d
Licensed under the Apache License, Version 2.0 (the "License");
Packit 63bb0d
you may not use this file except in compliance with the License.
Packit 63bb0d
You may obtain a copy of the License at
Packit 63bb0d
Packit 63bb0d
    http://www.apache.org/licenses/LICENSE-2.0
Packit 63bb0d
Packit 63bb0d
Unless required by applicable law or agreed to in writing, software
Packit 63bb0d
distributed under the License is distributed on an "AS IS" BASIS,
Packit 63bb0d
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
Packit 63bb0d
See the License for the specific language governing permissions and
Packit 63bb0d
limitations under the License.
Packit 63bb0d
*/
Packit 63bb0d
Packit 63bb0d
package object
Packit 63bb0d
Packit 63bb0d
import (
Packit 63bb0d
	"bufio"
Packit 63bb0d
	"bytes"
Packit 63bb0d
	"context"
Packit 63bb0d
	"fmt"
Packit 63bb0d
	"io"
Packit 63bb0d
	"log"
Packit 63bb0d
	"path"
Packit 63bb0d
	"strings"
Packit 63bb0d
Packit 63bb0d
	"github.com/vmware/govmomi/vim25/progress"
Packit 63bb0d
	"github.com/vmware/govmomi/vim25/soap"
Packit 63bb0d
)
Packit 63bb0d
Packit 63bb0d
// DatastoreFileManager combines FileManager and VirtualDiskManager to manage files on a Datastore
Packit 63bb0d
type DatastoreFileManager struct {
Packit 63bb0d
	Datacenter         *Datacenter
Packit 63bb0d
	Datastore          *Datastore
Packit 63bb0d
	FileManager        *FileManager
Packit 63bb0d
	VirtualDiskManager *VirtualDiskManager
Packit 63bb0d
Packit 63bb0d
	Force            bool
Packit 63bb0d
	DatacenterTarget *Datacenter
Packit 63bb0d
}
Packit 63bb0d
Packit 63bb0d
// NewFileManager creates a new instance of DatastoreFileManager
Packit 63bb0d
func (d Datastore) NewFileManager(dc *Datacenter, force bool) *DatastoreFileManager {
Packit 63bb0d
	c := d.Client()
Packit 63bb0d
Packit 63bb0d
	m := &DatastoreFileManager{
Packit 63bb0d
		Datacenter:         dc,
Packit 63bb0d
		Datastore:          &d,
Packit 63bb0d
		FileManager:        NewFileManager(c),
Packit 63bb0d
		VirtualDiskManager: NewVirtualDiskManager(c),
Packit 63bb0d
		Force:              force,
Packit 63bb0d
		DatacenterTarget:   dc,
Packit 63bb0d
	}
Packit 63bb0d
Packit 63bb0d
	return m
Packit 63bb0d
}
Packit 63bb0d
Packit 63bb0d
func (m *DatastoreFileManager) WithProgress(ctx context.Context, s progress.Sinker) context.Context {
Packit 63bb0d
	return context.WithValue(ctx, m, s)
Packit 63bb0d
}
Packit 63bb0d
Packit 63bb0d
func (m *DatastoreFileManager) wait(ctx context.Context, task *Task) error {
Packit 63bb0d
	var logger progress.Sinker
Packit 63bb0d
	if s, ok := ctx.Value(m).(progress.Sinker); ok {
Packit 63bb0d
		logger = s
Packit 63bb0d
	}
Packit 63bb0d
	_, err := task.WaitForResult(ctx, logger)
Packit 63bb0d
	return err
Packit 63bb0d
}
Packit 63bb0d
Packit 63bb0d
// Delete dispatches to the appropriate Delete method based on file name extension
Packit 63bb0d
func (m *DatastoreFileManager) Delete(ctx context.Context, name string) error {
Packit 63bb0d
	switch path.Ext(name) {
Packit 63bb0d
	case ".vmdk":
Packit 63bb0d
		return m.DeleteVirtualDisk(ctx, name)
Packit 63bb0d
	default:
Packit 63bb0d
		return m.DeleteFile(ctx, name)
Packit 63bb0d
	}
Packit 63bb0d
}
Packit 63bb0d
Packit 63bb0d
// DeleteFile calls FileManager.DeleteDatastoreFile
Packit 63bb0d
func (m *DatastoreFileManager) DeleteFile(ctx context.Context, name string) error {
Packit 63bb0d
	p := m.Path(name)
Packit 63bb0d
Packit 63bb0d
	task, err := m.FileManager.DeleteDatastoreFile(ctx, p.String(), m.Datacenter)
Packit 63bb0d
	if err != nil {
Packit 63bb0d
		return err
Packit 63bb0d
	}
Packit 63bb0d
Packit 63bb0d
	return m.wait(ctx, task)
Packit 63bb0d
}
Packit 63bb0d
Packit 63bb0d
// DeleteVirtualDisk calls VirtualDiskManager.DeleteVirtualDisk
Packit 63bb0d
// Regardless of the Datastore type, DeleteVirtualDisk will fail if 'ddb.deletable=false',
Packit 63bb0d
// so if Force=true this method attempts to set 'ddb.deletable=true' before starting the delete task.
Packit 63bb0d
func (m *DatastoreFileManager) DeleteVirtualDisk(ctx context.Context, name string) error {
Packit 63bb0d
	p := m.Path(name)
Packit 63bb0d
Packit 63bb0d
	var merr error
Packit 63bb0d
Packit 63bb0d
	if m.Force {
Packit 63bb0d
		merr = m.markDiskAsDeletable(ctx, p)
Packit 63bb0d
	}
Packit 63bb0d
Packit 63bb0d
	task, err := m.VirtualDiskManager.DeleteVirtualDisk(ctx, p.String(), m.Datacenter)
Packit 63bb0d
	if err != nil {
Packit 63bb0d
		log.Printf("markDiskAsDeletable(%s): %s", p, merr)
Packit 63bb0d
		return err
Packit 63bb0d
	}
Packit 63bb0d
Packit 63bb0d
	return m.wait(ctx, task)
Packit 63bb0d
}
Packit 63bb0d
Packit 63bb0d
// CopyFile calls FileManager.CopyDatastoreFile
Packit 63bb0d
func (m *DatastoreFileManager) CopyFile(ctx context.Context, src string, dst string) error {
Packit 63bb0d
	srcp := m.Path(src)
Packit 63bb0d
	dstp := m.Path(dst)
Packit 63bb0d
Packit 63bb0d
	task, err := m.FileManager.CopyDatastoreFile(ctx, srcp.String(), m.Datacenter, dstp.String(), m.DatacenterTarget, m.Force)
Packit 63bb0d
	if err != nil {
Packit 63bb0d
		return err
Packit 63bb0d
	}
Packit 63bb0d
Packit 63bb0d
	return m.wait(ctx, task)
Packit 63bb0d
}
Packit 63bb0d
Packit 63bb0d
// Copy dispatches to the appropriate FileManager or VirtualDiskManager Copy method based on file name extension
Packit 63bb0d
func (m *DatastoreFileManager) Copy(ctx context.Context, src string, dst string) error {
Packit 63bb0d
	srcp := m.Path(src)
Packit 63bb0d
	dstp := m.Path(dst)
Packit 63bb0d
Packit 63bb0d
	f := m.FileManager.CopyDatastoreFile
Packit 63bb0d
Packit 63bb0d
	if srcp.IsVMDK() {
Packit 63bb0d
		// types.VirtualDiskSpec=nil as it is not implemented by vCenter
Packit 63bb0d
		f = func(ctx context.Context, src string, srcDC *Datacenter, dst string, dstDC *Datacenter, force bool) (*Task, error) {
Packit 63bb0d
			return m.VirtualDiskManager.CopyVirtualDisk(ctx, src, srcDC, dst, dstDC, nil, force)
Packit 63bb0d
		}
Packit 63bb0d
	}
Packit 63bb0d
Packit 63bb0d
	task, err := f(ctx, srcp.String(), m.Datacenter, dstp.String(), m.DatacenterTarget, m.Force)
Packit 63bb0d
	if err != nil {
Packit 63bb0d
		return err
Packit 63bb0d
	}
Packit 63bb0d
Packit 63bb0d
	return m.wait(ctx, task)
Packit 63bb0d
}
Packit 63bb0d
Packit 63bb0d
// MoveFile calls FileManager.MoveDatastoreFile
Packit 63bb0d
func (m *DatastoreFileManager) MoveFile(ctx context.Context, src string, dst string) error {
Packit 63bb0d
	srcp := m.Path(src)
Packit 63bb0d
	dstp := m.Path(dst)
Packit 63bb0d
Packit 63bb0d
	task, err := m.FileManager.MoveDatastoreFile(ctx, srcp.String(), m.Datacenter, dstp.String(), m.DatacenterTarget, m.Force)
Packit 63bb0d
	if err != nil {
Packit 63bb0d
		return err
Packit 63bb0d
	}
Packit 63bb0d
Packit 63bb0d
	return m.wait(ctx, task)
Packit 63bb0d
}
Packit 63bb0d
Packit 63bb0d
// Move dispatches to the appropriate FileManager or VirtualDiskManager Move method based on file name extension
Packit 63bb0d
func (m *DatastoreFileManager) Move(ctx context.Context, src string, dst string) error {
Packit 63bb0d
	srcp := m.Path(src)
Packit 63bb0d
	dstp := m.Path(dst)
Packit 63bb0d
Packit 63bb0d
	f := m.FileManager.MoveDatastoreFile
Packit 63bb0d
Packit 63bb0d
	if srcp.IsVMDK() {
Packit 63bb0d
		f = m.VirtualDiskManager.MoveVirtualDisk
Packit 63bb0d
	}
Packit 63bb0d
Packit 63bb0d
	task, err := f(ctx, srcp.String(), m.Datacenter, dstp.String(), m.DatacenterTarget, m.Force)
Packit 63bb0d
	if err != nil {
Packit 63bb0d
		return err
Packit 63bb0d
	}
Packit 63bb0d
Packit 63bb0d
	return m.wait(ctx, task)
Packit 63bb0d
}
Packit 63bb0d
Packit 63bb0d
// Path converts path name to a DatastorePath
Packit 63bb0d
func (m *DatastoreFileManager) Path(name string) *DatastorePath {
Packit 63bb0d
	var p DatastorePath
Packit 63bb0d
Packit 63bb0d
	if !p.FromString(name) {
Packit 63bb0d
		p.Path = name
Packit 63bb0d
		p.Datastore = m.Datastore.Name()
Packit 63bb0d
	}
Packit 63bb0d
Packit 63bb0d
	return &p
Packit 63bb0d
}
Packit 63bb0d
Packit 63bb0d
func (m *DatastoreFileManager) markDiskAsDeletable(ctx context.Context, path *DatastorePath) error {
Packit 63bb0d
	r, _, err := m.Datastore.Download(ctx, path.Path, &soap.DefaultDownload)
Packit 63bb0d
	if err != nil {
Packit 63bb0d
		return err
Packit 63bb0d
	}
Packit 63bb0d
Packit 63bb0d
	defer r.Close()
Packit 63bb0d
Packit 63bb0d
	hasFlag := false
Packit 63bb0d
	buf := new(bytes.Buffer)
Packit 63bb0d
Packit 63bb0d
	s := bufio.NewScanner(&io.LimitedReader{R: r, N: 2048}) // should be only a few hundred bytes, limit to be sure
Packit 63bb0d
Packit 63bb0d
	for s.Scan() {
Packit 63bb0d
		line := s.Text()
Packit 63bb0d
		if strings.HasPrefix(line, "ddb.deletable") {
Packit 63bb0d
			hasFlag = true
Packit 63bb0d
			continue
Packit 63bb0d
		}
Packit 63bb0d
Packit 63bb0d
		fmt.Fprintln(buf, line)
Packit 63bb0d
	}
Packit 63bb0d
Packit 63bb0d
	if err := s.Err(); err != nil {
Packit 63bb0d
		return err // any error other than EOF
Packit 63bb0d
	}
Packit 63bb0d
Packit 63bb0d
	if !hasFlag {
Packit 63bb0d
		return nil // already deletable, so leave as-is
Packit 63bb0d
	}
Packit 63bb0d
Packit 63bb0d
	// rewrite the .vmdk with ddb.deletable flag removed (the default is true)
Packit 63bb0d
	return m.Datastore.Upload(ctx, buf, path.Path, &soap.DefaultUpload)
Packit 63bb0d
}