|
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 |
}
|