// Copyright 2019 Yunion // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package manager import ( "sync" "yunion.io/x/jsonutils" "yunion.io/x/log" "yunion.io/x/pkg/errors" "yunion.io/x/onecloud/pkg/util/losetup" ) var ( managerObj iManager managerInitLock sync.Mutex ) func Init() { managerInitLock.Lock() defer managerInitLock.Unlock() if managerObj == nil { managerObj = newManager() } } func ListDevices() (*losetup.Devices, error) { return managerObj.listDevices() } func AttachDevice(filePath string, partScan bool) (*losetup.Device, error) { return managerObj.attachDevice(filePath, partScan) } func DetachDevice(filePath string) error { return managerObj.detachDevice(filePath) } type iManager interface { listDevices() (*losetup.Devices, error) attachDevice(filePath string, partScan bool) (*losetup.Device, error) detachDevice(devPath string) error } type execType string const ( EXEC_ATTACH execType = "attach" EXEC_DETACH execType = "detach" ) type execAttach struct { FilePath string PartScan bool } type execDetach struct { DevicePath string } type execOption struct { Type execType Attach *execAttach Detach *execDetach result chan execResult } type execResult struct { error error attachedDevice *losetup.Device } func newAttachOption(filePath string, partScan bool) *execOption { return &execOption{ Type: EXEC_ATTACH, result: make(chan execResult), Attach: &execAttach{ FilePath: filePath, PartScan: partScan, }, } } func newDetachOption(devPath string) *execOption { return &execOption{ Type: EXEC_DETACH, result: make(chan execResult), Detach: &execDetach{ DevicePath: devPath, }, } } type manager struct { execOptCh chan *execOption loopMan ILoopManager } func newManager() iManager { loopMan, err := newLoopManager() if err != nil { log.Fatalf("new loop manager error: %v", err) } m := &manager{ execOptCh: make(chan *execOption), loopMan: loopMan, } go func() { m.startExec() }() return m } func (m *manager) listDevices() (*losetup.Devices, error) { return losetup.ListDevices() } func (m *manager) attachDevice(filePath string, partScan bool) (*losetup.Device, error) { opt := newAttachOption(filePath, partScan) m.execOptCh <- opt result := <-opt.result return result.attachedDevice, result.error } func (m *manager) detachDevice(devPath string) error { opt := newDetachOption(devPath) m.execOptCh <- opt result := <-opt.result return result.error } func (m *manager) startExec() { for execOpt := range m.execOptCh { switch execOpt.Type { case EXEC_ATTACH: log.Infof("attach %s", jsonutils.Marshal(execOpt.Attach)) dev, err := m.loopMan.AttachDevice(execOpt.Attach.FilePath, execOpt.Attach.PartScan) execOpt.result <- execResult{ attachedDevice: dev, error: err, } case EXEC_DETACH: log.Infof("detach %s", jsonutils.Marshal(execOpt.Detach)) err := m.loopMan.DetachDevice(execOpt.Detach.DevicePath) execOpt.result <- execResult{ error: err, } default: err := errors.Errorf("unknown exec type: %s", execOpt.Type) execOpt.result <- execResult{ error: err, } } } }