| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326 |
- // 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 nbd
- import (
- "fmt"
- "io/ioutil"
- "os"
- "regexp"
- "strings"
- "sync"
- "time"
- "yunion.io/x/log"
- "yunion.io/x/pkg/errors"
- "yunion.io/x/pkg/util/stringutils"
- "yunion.io/x/onecloud/pkg/hostman/guestfs/kvmpart"
- "yunion.io/x/onecloud/pkg/util/fileutils2"
- "yunion.io/x/onecloud/pkg/util/procutils"
- )
- const (
- PATH_TYPE_UNKNOWN = 0
- LVM_PATH = 1
- NON_LVM_PATH = 2
- )
- type SImageProp struct {
- HasLVMPartition bool
- lock *sync.Mutex
- }
- type SLVMImageConnectUniqueToolSet struct {
- *sync.Map
- lock *sync.Mutex
- }
- func NewLVMImageConnectUniqueToolSet() *SLVMImageConnectUniqueToolSet {
- return &SLVMImageConnectUniqueToolSet{
- Map: &sync.Map{},
- lock: &sync.Mutex{},
- }
- }
- func (s *SLVMImageConnectUniqueToolSet) CacheNonLvmImagePath(imagePath string) {
- if im, ok := s.Load(imagePath); ok {
- imgProp := im.(*SImageProp)
- imgProp.HasLVMPartition = false
- }
- }
- func (s *SLVMImageConnectUniqueToolSet) loadImagePath(imagePath string) (*SImageProp, bool) {
- s.lock.Lock()
- defer s.lock.Unlock()
- im, ok := s.Load(imagePath)
- if !ok {
- imgProp := &SImageProp{
- HasLVMPartition: true, // set has lvm partition default
- lock: new(sync.Mutex),
- }
- s.Store(imagePath, imgProp)
- return imgProp, false
- } else {
- return im.(*SImageProp), ok
- }
- }
- func (s *SLVMImageConnectUniqueToolSet) Acquire(imagePath string) (int, *sync.Mutex) {
- var lock *sync.Mutex
- pathType := PATH_TYPE_UNKNOWN
- imgProp, ok := s.loadImagePath(imagePath)
- if imgProp.HasLVMPartition {
- if ok {
- pathType = LVM_PATH
- }
- lock = imgProp.lock
- } else {
- pathType = NON_LVM_PATH
- }
- return pathType, lock
- }
- type SKVMGuestLVMPartition struct {
- partDev string
- originVgname string
- vgname string
- // if need to modify the name when putting down
- needChangeName bool
- vgid string
- }
- func findVgname(partDev string) string {
- output, err := procutils.NewCommand("pvscan").Output()
- if err != nil {
- log.Errorf("%s", output)
- return ""
- }
- re := regexp.MustCompile(`\s+`)
- for _, line := range strings.Split(string(output), "\n") {
- data := re.Split(strings.TrimSpace(line), -1)
- if len(data) >= 4 && data[1] == partDev {
- return data[3]
- }
- }
- return ""
- }
- type SVG struct {
- Id string
- Name string
- }
- func findVg(partDev string) (SVG, error) {
- command := procutils.NewCommand("vgs", "-v", "--devices", partDev)
- output, err := command.Output()
- if err != nil {
- return SVG{}, errors.Wrapf(err, "unable to exec command %q", command)
- }
- log.Debugf("command: %s\noutptu: %s", command, output)
- outputStr := string(output)
- r := regexp.MustCompile("WARNING: Device mismatch detected for .* which is accessing .* instead of .*.")
- ret := r.FindStringSubmatch(outputStr)
- if len(ret) > 0 {
- return SVG{}, fmt.Errorf("VG conflicts with the VG UUID of the host")
- }
- lines := strings.Split(strings.TrimSpace(outputStr), "\n")
- if len(lines) <= 1 {
- return SVG{}, fmt.Errorf("unable to find vg, output is %q", output)
- }
- data := regexp.MustCompile(`\s+`).Split(strings.TrimSpace(lines[len(lines)-1]), -1)
- if len(data) < 1 || len(data) < 9 {
- return SVG{}, fmt.Errorf("The output is not as expected: %q", output)
- }
- return SVG{data[8], data[0]}, nil
- }
- func NewKVMGuestLVMPartition(partDev string, vg SVG) *SKVMGuestLVMPartition {
- return &SKVMGuestLVMPartition{
- partDev: partDev,
- originVgname: vg.Name,
- vgname: uuidWithoutLine(),
- vgid: vg.Id,
- }
- }
- func uuidWithoutLine() string {
- uuid := stringutils.UUID4()
- return strings.ReplaceAll(uuid, "-", "")
- }
- func (p *SKVMGuestLVMPartition) SetupDevice() bool {
- if len(p.vgid) == 0 {
- return false
- }
- if p.vgRename(p.vgid, p.vgname) {
- p.needChangeName = true
- } else {
- p.vgname = p.originVgname
- p.needChangeName = false
- }
- if p.vgActivate(true) {
- return true
- }
- return false
- }
- func (p *SKVMGuestLVMPartition) FindPartitions() []*kvmpart.SKVMGuestDiskPartition {
- parts := []*kvmpart.SKVMGuestDiskPartition{}
- // try /dev/{vgname}/{lvname}
- files, err := ioutil.ReadDir("/dev/" + p.vgname)
- if err == nil {
- for _, f := range files {
- partPath := fmt.Sprintf("/dev/%s/%s", p.vgname, f.Name())
- part := kvmpart.NewKVMGuestDiskPartition(partPath, p.partDev, true)
- parts = append(parts, part)
- }
- return parts
- }
- if !os.IsNotExist(err) {
- log.Errorf("unable to readir /dev/%s: %v", p.vgname, err)
- return nil
- }
- log.Debugf("unable to read dir '/dev/%s': %v", p.vgname, err)
- // try /dev/mapper/{vgname}-{lvname}
- lvs, err := p.lvs()
- if err != nil {
- log.Errorf("unable to list lvs: %v", err)
- return nil
- }
- for _, lvname := range lvs {
- path := fmt.Sprintf("/dev/mapper/%s-%s", p.vgname, lvname)
- _, err := os.Lstat(path)
- if err == nil {
- part := kvmpart.NewKVMGuestDiskPartition(path, p.partDev, true)
- parts = append(parts, part)
- continue
- }
- log.Errorf("unable to ls %s: %v", path, err)
- }
- return parts
- }
- func (p *SKVMGuestLVMPartition) UmountPartitions() error {
- files, err := ioutil.ReadDir("/dev/" + p.vgname)
- if err == nil {
- for _, f := range files {
- partPath := fmt.Sprintf("/dev/%s/%s", p.vgname, f.Name())
- out, err := procutils.NewCommand("umount", partPath).Output()
- if err != nil {
- log.Errorf("failed umount part %s: %s", partPath, out)
- }
- }
- }
- if !os.IsNotExist(err) {
- return errors.Errorf("unable to readir /dev/%s: %v", p.vgname, err)
- }
- lvs, err := p.lvs()
- if err != nil {
- return errors.Errorf("unable to list lvs: %v", err)
- }
- for _, lvname := range lvs {
- partPath := fmt.Sprintf("/dev/mapper/%s-%s", p.vgname, lvname)
- if fileutils2.Exists(partPath) {
- out, err := procutils.NewCommand("umount", partPath).Output()
- if err != nil {
- log.Errorf("failed umount part %s: %s", partPath, out)
- }
- }
- }
- return nil
- }
- var gexp *regexp.Regexp = regexp.MustCompile(`\s+`)
- func (p *SKVMGuestLVMPartition) lvs() ([]string, error) {
- command := procutils.NewCommand("lvs", p.vgname)
- output, err := command.Output()
- if err != nil {
- return nil, errors.Wrapf(err, "unable to exec %q command", command)
- }
- lines := strings.Split(strings.TrimSpace(string(output)), "\n")
- if len(lines) == 0 {
- return nil, errors.Wrapf(err, "command: %q, no output", command)
- }
- tableHeader := gexp.Split(strings.TrimSpace(lines[0]), -1)
- if tableHeader[0] != "LV" {
- return nil, fmt.Errorf("command: %q, unexpected output: %q", command, output)
- }
- lvname := make([]string, 0, len(lines)-1)
- for i := 1; i < len(lines); i++ {
- tableLine := gexp.Split(strings.TrimSpace(lines[i]), -1)
- lvname = append(lvname, tableLine[0])
- }
- return lvname, nil
- }
- func (p *SKVMGuestLVMPartition) PutdownDevice() bool {
- var deactivate = false
- for i := 0; i < 10; i++ {
- if !p.vgActivate(false) {
- log.Errorf("failed deactivate %s", p.vgname)
- if err := p.UmountPartitions(); err != nil {
- log.Warningf("failed umount partitions %s", err)
- }
- time.Sleep(time.Second * 3)
- } else {
- deactivate = true
- break
- }
- }
- if !deactivate {
- return false
- }
- if len(p.originVgname) == 0 || !p.needChangeName {
- return true
- }
- if p.vgRename(p.vgname, p.originVgname) {
- return true
- }
- return false
- }
- func (p *SKVMGuestLVMPartition) vgActivate(activate bool) bool {
- param := "-an"
- if activate {
- param = "-ay"
- }
- output, err := procutils.NewCommand("vgchange", param, p.vgname).Output()
- if err != nil {
- log.Errorf("%s", output)
- return false
- }
- if out, err := procutils.NewCommand("vgchange", "--refresh").Output(); err != nil {
- log.Errorf("vgchange refresh failed: %s, %s", out, err)
- }
- return true
- }
- func (p *SKVMGuestLVMPartition) vgRename(oldname, newname string) bool {
- command := procutils.NewCommand("vgrename", "--devices", p.partDev, oldname, newname)
- output, err := command.Output()
- if err != nil {
- log.Errorf("unable to exec command: %q, error: %v, output: %q", command, err, output)
- return false
- }
- log.Infof("VG rename succ from %s to %s", oldname, newname)
- return true
- }
|