mirror of
https://github.com/yunionio/cloudpods.git
synced 2026-09-21 14:19:49 +08:00
2018-12-17 ~ 81-21-8102
This commit is contained in:
@@ -0,0 +1,105 @@
|
||||
package fstabutils
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"yunion.io/x/log"
|
||||
)
|
||||
|
||||
type SFSRecord struct {
|
||||
Dev string
|
||||
Mount string
|
||||
Fs string
|
||||
Opt string
|
||||
Dump string
|
||||
Pas string
|
||||
}
|
||||
|
||||
func FSRecord(line string) *SFSRecord {
|
||||
data := regexp.MustCompile(`\s+`).Split(line, -1)
|
||||
if len(data) > 5 {
|
||||
return &SFSRecord{data[0], data[1], data[2], data[3], data[4], data[5]}
|
||||
}
|
||||
log.Errorf("Invalid fstab record %s", line)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (fsr *SFSRecord) String() string {
|
||||
return fmt.Sprintf("%s\t%s\t%s\t%s\t%s\t%s",
|
||||
fsr.Dev, fsr.Mount, fsr.Fs, fsr.Opt, fsr.Dump, fsr.Pas)
|
||||
}
|
||||
|
||||
var VDISK_PREFIX = "/dev/vd"
|
||||
|
||||
type FsTab []*SFSRecord
|
||||
|
||||
func FSTabFile(content string) *FsTab {
|
||||
if len(content) > 0 {
|
||||
var res = make(FsTab, 0)
|
||||
lines := strings.Split(content, "\n")
|
||||
for _, line := range lines {
|
||||
line = strings.TrimSpace(line)
|
||||
if len(line) > 0 && line[0] != '#' {
|
||||
if fsr := FSRecord(line); fsr != nil {
|
||||
res = append(res, fsr)
|
||||
}
|
||||
}
|
||||
}
|
||||
return &res
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ft *FsTab) IsExists(dev string) bool {
|
||||
for _, f := range *ft {
|
||||
if f.Dev == dev {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (ft *FsTab) AddFsrec(line string) {
|
||||
rec := FSRecord(line)
|
||||
if rec != nil {
|
||||
*ft = append(*ft, rec)
|
||||
}
|
||||
}
|
||||
|
||||
func (ft *FsTab) RemoveDevices(devCnt int) {
|
||||
var newList = make(FsTab, 0)
|
||||
for _, f := range *ft {
|
||||
if strings.HasPrefix(f.Dev, VDISK_PREFIX) {
|
||||
devChar := f.Dev[len(VDISK_PREFIX)]
|
||||
devIdx := devChar - 'a'
|
||||
if devIdx >= 0 && int(devIdx) < devCnt {
|
||||
newList = append(newList, f)
|
||||
}
|
||||
} else {
|
||||
newList = append(newList, f)
|
||||
}
|
||||
}
|
||||
ft = &newList
|
||||
}
|
||||
|
||||
func (ft *FsTab) ToConf() string {
|
||||
var res string
|
||||
for _, f := range *ft {
|
||||
res += fmt.Sprintf("%s\n", f)
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
/* TODO: test
|
||||
if __name__ == '__main__':
|
||||
with open('/etc/fstab') as f:
|
||||
cont = f.read(4096)
|
||||
print cont
|
||||
fstab = FSTabFile(cont)
|
||||
print fstab.is_exists('/dev/sdc2')
|
||||
fstab.add_fsrec('/dev/sdd1 /data ext4 defaults 0 0')
|
||||
fstab.add_fsrec('/dev/sdd2')
|
||||
print fstab.to_conf()
|
||||
*/
|
||||
@@ -0,0 +1,13 @@
|
||||
package guestfs
|
||||
|
||||
type SAndroidRootFs struct {
|
||||
*SGuestRootFsDriver
|
||||
}
|
||||
|
||||
func NewAndroidRootFs(part *SKVMGuestDiskPartition) IRootFsDriver {
|
||||
return &SAndroidRootFs{SGuestRootFsDriver: NewGuestRootFsDriver(part).(*SGuestRootFsDriver)}
|
||||
}
|
||||
|
||||
func init() {
|
||||
rootfsDrivers = append(rootfsDrivers, NewAndroidRootFs)
|
||||
}
|
||||
+264
-62
@@ -2,96 +2,298 @@ package guestfs
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"math/rand"
|
||||
"os"
|
||||
"path"
|
||||
"strings"
|
||||
"path/filepath"
|
||||
"syscall"
|
||||
|
||||
"yunion.io/x/jsonutils"
|
||||
"yunion.io/x/log"
|
||||
"yunion.io/x/onecloud/pkg/cloudcommon/sshkeys"
|
||||
"yunion.io/x/onecloud/pkg/hostman"
|
||||
"yunion.io/x/pkg/util/netutils"
|
||||
)
|
||||
|
||||
type SDeployInfo struct {
|
||||
publicKey *sshkeys.SSHKeys
|
||||
deploys jsonutils.JSONObject
|
||||
deploys []jsonutils.JSONObject
|
||||
password string
|
||||
isInit bool
|
||||
enableTty bool
|
||||
}
|
||||
|
||||
type SLocalGuestFS struct {
|
||||
mountPath string
|
||||
readOnly bool
|
||||
}
|
||||
type newRootFsDriverFunc func(part *SKVMGuestDiskPartition) IRootFsDriver
|
||||
|
||||
func (f *SLocalGuestFS) isReadonly() bool {
|
||||
log.Infof("Test if read-only fs ...")
|
||||
var filename = fmt.Sprint("./%f", rand.Float32())
|
||||
if err := hostman.FilePutContents(filename, fmt.Sprint("%f", rand.Float32()), false); err == nil {
|
||||
f.Remove(filename, false)
|
||||
return false
|
||||
} else {
|
||||
log.Errorf("File system is readonly: %s", err)
|
||||
f.readOnly = true
|
||||
return true
|
||||
}
|
||||
}
|
||||
var rootfsDrivers = make([]newRootFsDriverFunc, 0)
|
||||
|
||||
func (f *SLocalGuestFS) getLocalPath(sPath string, caseInsensitive bool) string {
|
||||
var fullPath = f.mountPath
|
||||
pathSegs := strings.Split(sPath, "/")
|
||||
for _, seg := range pathSegs {
|
||||
if len(seg) > 0 {
|
||||
var realSeg string
|
||||
files, _ := ioutil.ReadDir(fullPath)
|
||||
for _, file := range files {
|
||||
var f = file.Name()
|
||||
if f == seg || (caseInsensitive && (strings.ToLower(f)) == strings.ToLower(seg)) ||
|
||||
(seg[len(seg)-1] == '*' && strings.HasPrefix(f, seg[:len(seg)-1])) ||
|
||||
(caseInsensitive && strings.HasPrefix(strings.ToLower(f),
|
||||
strings.ToLower(seg[:len(seg)]))) {
|
||||
realSeg = f
|
||||
break
|
||||
}
|
||||
}
|
||||
if len(realSeg) > 0 {
|
||||
fullPath = path.Join(fullPath, realSeg)
|
||||
} else {
|
||||
return ""
|
||||
}
|
||||
func DetectRootFs(part *SKVMGuestDiskPartition) IRootFsDriver {
|
||||
for _, newDriverFunc := range rootfsDrivers {
|
||||
d := newDriverFunc(part)
|
||||
if testRootfs(d) {
|
||||
return d
|
||||
}
|
||||
}
|
||||
return fullPath
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *SLocalGuestFS) Remove(path string, caseInsensitive bool) {
|
||||
path = f.getLocalPath(path, caseInsensitive)
|
||||
if len(path) > 0 {
|
||||
os.Remove(path)
|
||||
func testRootfs(d IRootFsDriver) bool {
|
||||
caseInsensitive := d.IsFsCaseInsensitive()
|
||||
for _, rd := range d.RootSignatures() {
|
||||
if !d.GetPartition().Exists(rd, caseInsensitive) {
|
||||
log.Infof("[%s] test root fs: %s not exists", d, rd)
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func NewLocalGuestFS(mountPath string) *SLocalGuestFS {
|
||||
var ret = new(SLocalGuestFS)
|
||||
ret.mountPath = mountPath
|
||||
return ret
|
||||
for _, rd := range d.RootExcludeSignatures() {
|
||||
if d.GetPartition().Exists(rd, caseInsensitive) {
|
||||
log.Infof("[%s] test root fs: %s exists, test failed", d, rd)
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
type IRootFsDriver interface {
|
||||
GetPartition() *SKVMGuestDiskPartition
|
||||
String() string
|
||||
TestRootfs() bool
|
||||
|
||||
IsFsCaseInsensitive() bool
|
||||
RootSignatures() []string
|
||||
RootExcludeSignatures() []string
|
||||
GetReleaseInfo() []string
|
||||
GetOs() string
|
||||
DeployFiles([]jsonutils.JSONObject) error
|
||||
DeployHostname(hn, domain string) error
|
||||
DeployHost(hn, domain string, ips []string) error
|
||||
DeployNetworkingScripts([]jsonutils.JSONObject) error
|
||||
DeployStandbyNetworkingScripts(nics, nicsStandby []jsonutils.JSONObject) error
|
||||
DeployUdevSubsystemScripts() error
|
||||
DeployFstabScripts([]jsonutils.JSONObject) error
|
||||
GetLoginAccount() string
|
||||
DeployPublicKey(string, *sshkeys.SSHKeys) error
|
||||
ChangeUserPasswd(account, gid, publicKey, password string) string
|
||||
DeployYunionroot(*sshkeys.SSHKeys) error
|
||||
EnableSerialConsole(*jsonutils.JSONDict) error
|
||||
DisableSerialConsole() error
|
||||
CommitChanges() error
|
||||
|
||||
DeployGuestFs(IRootFsDriver, *jsonutils.JSONDict, *SDeployInfo) (jsonutils.JSONObject, error)
|
||||
}
|
||||
|
||||
var rootfsDrivers map[string]IRootFsDriver
|
||||
type SGuestRootFsDriver struct {
|
||||
rootFs *SKVMGuestDiskPartition
|
||||
}
|
||||
|
||||
func DetectRootFs(part *SKVMGuestDiskPartition) IRootFsDriver {
|
||||
for k, v := range rootfsDrivers {
|
||||
if v.TestRootfs(part) {
|
||||
return v
|
||||
func NewGuestRootFsDriver(rootFs *SKVMGuestDiskPartition) IRootFsDriver {
|
||||
return &SGuestRootFsDriver{rootFs}
|
||||
}
|
||||
|
||||
func (d *SGuestRootFsDriver) GetPartition() *SKVMGuestDiskPartition {
|
||||
return d.rootFs
|
||||
}
|
||||
|
||||
func (d *SGuestRootFsDriver) String() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func (d *SGuestRootFsDriver) IsFsCaseInsensitive() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (d *SGuestRootFsDriver) RootSignatures() []string {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *SGuestRootFsDriver) RootExcludeSignatures() []string {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *SGuestRootFsDriver) GetReleaseInfo() []string {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *SGuestRootFsDriver) GetOs() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func (d *SGuestRootFsDriver) DeployFiles(deploys []jsonutils.JSONObject) error {
|
||||
caseInsensitive := d.IsFsCaseInsensitive()
|
||||
for _, deploy := range deploys {
|
||||
var modAppend = false
|
||||
if action, _ := deploy.GetString("action"); action == "append" {
|
||||
modAppend = true
|
||||
}
|
||||
sPath, err := deploy.GetString("path")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
dirname := filepath.Dir(sPath)
|
||||
if !d.rootFs.Exists(sPath, caseInsensitive) {
|
||||
modeRWXOwner := syscall.S_IRUSR | syscall.S_IWUSR | syscall.S_IXUSR
|
||||
err := d.rootFs.Mkdir(dirname, modeRWXOwner, caseInsensitive)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if content, err := deploy.GetString("content"); err != nil {
|
||||
err := d.rootFs.FilePutContents(sPath, content, modAppend, caseInsensitive)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *SGuestRootFsDriver) DeployHostname(hn, domain string) error {
|
||||
return fmt.Errorf("Not Implemented")
|
||||
}
|
||||
|
||||
func (d *SGuestRootFsDriver) DeployHost(hn, domain string, ips []string) error {
|
||||
return fmt.Errorf("Not Implemented")
|
||||
}
|
||||
|
||||
func (d *SGuestRootFsDriver) DeployNetworkingScripts([]jsonutils.JSONObject) error {
|
||||
return fmt.Errorf("Not Implemented")
|
||||
}
|
||||
|
||||
func (d *SGuestRootFsDriver) DeployStandbyNetworkingScripts(nics, nicsStandby []jsonutils.JSONObject) error {
|
||||
return fmt.Errorf("Not Implemented")
|
||||
}
|
||||
|
||||
func (d *SGuestRootFsDriver) DeployUdevSubsystemScripts() error {
|
||||
return fmt.Errorf("Not Implemented")
|
||||
}
|
||||
|
||||
func (d *SGuestRootFsDriver) DeployFstabScripts([]jsonutils.JSONObject) error {
|
||||
return fmt.Errorf("Not Implemented")
|
||||
}
|
||||
|
||||
func (d *SGuestRootFsDriver) GetLoginAccount() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func (d *SGuestRootFsDriver) DeployPublicKey(string, *sshkeys.SSHKeys) error {
|
||||
return fmt.Errorf("Not Implemented")
|
||||
}
|
||||
|
||||
func (d *SGuestRootFsDriver) ChangeUserPasswd(account, gid, publicKey, password string) string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func (d *SGuestRootFsDriver) DeployYunionroot(*sshkeys.SSHKeys) error {
|
||||
return fmt.Errorf("Not Implemented")
|
||||
}
|
||||
|
||||
func (d *SGuestRootFsDriver) EnableSerialConsole(*jsonutils.JSONDict) error {
|
||||
return fmt.Errorf("Not Implemented")
|
||||
}
|
||||
|
||||
func (d *SGuestRootFsDriver) DisableSerialConsole() error {
|
||||
return fmt.Errorf("Not Implemented")
|
||||
}
|
||||
|
||||
func (d *SGuestRootFsDriver) CommitChanges() error {
|
||||
return fmt.Errorf("Not Implemented")
|
||||
}
|
||||
|
||||
func (d *SGuestRootFsDriver) DeployGuestFs(rootfs IRootFsDriver, guestDesc *jsonutils.JSONDict,
|
||||
deployInfo *SDeployInfo) (jsonutils.JSONObject, error) {
|
||||
var ret = jsonutils.NewDict()
|
||||
var ips = make([]string, 0)
|
||||
var releaseInfo = rootfs.GetReleaseInfo()
|
||||
hn, _ := guestDesc.GetString("name")
|
||||
domain, _ := guestDesc.GetString("domain")
|
||||
gid, _ := guestDesc.GetString("uuid")
|
||||
nics, _ := guestDesc.GetArray("nisc")
|
||||
|
||||
var err error
|
||||
for _, n := range nics {
|
||||
ip, _ := n.GetString("ip")
|
||||
var addr netutils.IPV4Addr
|
||||
if addr, err = netutils.NewIPV4Addr(ip); err != nil {
|
||||
log.Errorln(err)
|
||||
return nil, err
|
||||
}
|
||||
if netutils.IsPrivate(addr) {
|
||||
ips = append(ips, ip)
|
||||
}
|
||||
}
|
||||
if releaseInfo != nil {
|
||||
ret.Set("distro", jsonutils.NewString(releaseInfo[0]))
|
||||
if len(releaseInfo) > 1 && len(releaseInfo[1]) > 0 {
|
||||
ret.Set("version", jsonutils.NewString(releaseInfo[1]))
|
||||
}
|
||||
if len(releaseInfo) > 2 && len(releaseInfo[2]) > 0 {
|
||||
ret.Set("arch", jsonutils.NewString(releaseInfo[2]))
|
||||
}
|
||||
if len(releaseInfo) > 3 && len(releaseInfo[3]) > 0 {
|
||||
ret.Set("language", jsonutils.NewString(releaseInfo[3]))
|
||||
}
|
||||
}
|
||||
ret.Set("os", jsonutils.NewString(rootfs.GetOs()))
|
||||
if d.rootFs.GetReadonly() {
|
||||
if len(deployInfo.deploys) > 0 {
|
||||
if err = rootfs.DeployFiles(deployInfo.deploys); err != nil {
|
||||
log.Errorln(err)
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
if err = rootfs.DeployHostname(hn, domain); err != nil {
|
||||
log.Errorln(err)
|
||||
return nil, err
|
||||
}
|
||||
if err = rootfs.DeployHost(hn, domain, ips); err != nil {
|
||||
log.Errorln(err)
|
||||
return nil, err
|
||||
}
|
||||
if err = rootfs.DeployNetworkingScripts(nics); err != nil {
|
||||
log.Errorln(err)
|
||||
return nil, err
|
||||
}
|
||||
if nicsStandby, e := guestDesc.GetArray("nics_standby"); e == nil {
|
||||
rootfs.DeployStandbyNetworkingScripts(nics, nicsStandby)
|
||||
}
|
||||
if err = rootfs.DeployUdevSubsystemScripts(); err != nil {
|
||||
log.Errorln(err)
|
||||
return nil, err
|
||||
}
|
||||
if deployInfo.isInit {
|
||||
disks, _ := guestDesc.GetArray("disks")
|
||||
if err = rootfs.DeployFstabScripts(disks); err != nil {
|
||||
log.Errorln(err)
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
if len(deployInfo.password) > 0 {
|
||||
if account := rootfs.GetLoginAccount(); len(account) > 0 {
|
||||
if err = rootfs.DeployPublicKey(account, deployInfo.publicKey); err != nil {
|
||||
log.Errorln(err)
|
||||
return nil, err
|
||||
}
|
||||
secret := rootfs.ChangeUserPasswd(account, gid,
|
||||
deployInfo.publicKey.PublicKey, deployInfo.password)
|
||||
if len(secret) > 0 {
|
||||
ret.Set("key", jsonutils.NewString(secret))
|
||||
}
|
||||
}
|
||||
}
|
||||
if err = rootfs.DeployYunionroot(deployInfo.publicKey); err != nil {
|
||||
log.Errorln(err)
|
||||
return nil, err
|
||||
}
|
||||
if deployInfo.enableTty {
|
||||
if err = rootfs.EnableSerialConsole(ret); err != nil {
|
||||
log.Errorln(err)
|
||||
return nil, err
|
||||
}
|
||||
} else {
|
||||
if err = rootfs.DisableSerialConsole(); err != nil {
|
||||
log.Errorln(err)
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
if err = rootfs.CommitChanges(); err != nil {
|
||||
log.Errorln(err)
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
package guestfs
|
||||
|
||||
type SEsxiRootFs struct {
|
||||
*SGuestRootFsDriver
|
||||
}
|
||||
|
||||
func NewEsxiRootFs(part *SKVMGuestDiskPartition) IRootFsDriver {
|
||||
return &SEsxiRootFs{SGuestRootFsDriver: NewGuestRootFsDriver(part).(*SGuestRootFsDriver)}
|
||||
}
|
||||
|
||||
func init() {
|
||||
rootfsDrivers = append(rootfsDrivers, NewEsxiRootFs)
|
||||
}
|
||||
@@ -2,8 +2,10 @@ package guestfs
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"yunion.io/x/log"
|
||||
"yunion.io/x/onecloud/pkg/hostman"
|
||||
@@ -46,9 +48,12 @@ func (p *SKVMGuestDiskPartition) Mount() bool {
|
||||
log.Errorf("SKVMGuestDiskPartition mount error: %s", err)
|
||||
return false
|
||||
}
|
||||
if p.isReadonly() {
|
||||
if p.IsReadonly() {
|
||||
log.Errorf("SKVMGuestDiskPartition %s is readonly, try mount as ro", p.partDev)
|
||||
p.Umount()
|
||||
e := p.Umount() // TODO
|
||||
if e != nil {
|
||||
log.Errorln(e)
|
||||
}
|
||||
err = p.mount(true)
|
||||
if err != nil {
|
||||
log.Errorf("SKVMGuestDiskPartition mount as ro error %s", err)
|
||||
@@ -113,3 +118,44 @@ func (p *SKVMGuestDiskPartition) fsck() error {
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *SKVMGuestDiskPartition) Exists(sPath string, caseInsensitive bool) bool {
|
||||
sPath = p.getLocalPath(sPath, caseInsensitive)
|
||||
if len(sPath) > 0 {
|
||||
if _, err := os.Stat(sPath); !os.IsNotExist(err) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (p *SKVMGuestDiskPartition) IsMounted() bool {
|
||||
if _, err := os.Stat(p.mountPath); os.IsNotExist(err) {
|
||||
return false
|
||||
}
|
||||
err := exec.Command("mountpoint", p.mountPath).Run()
|
||||
if err == nil {
|
||||
return true
|
||||
} else {
|
||||
log.Errorln(err)
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (p *SKVMGuestDiskPartition) Umount() bool {
|
||||
if p.IsMounted() {
|
||||
var tries = 0
|
||||
for tries < 10 {
|
||||
tries += 1
|
||||
err := exec.Command("umount", p.mountPath).Run()
|
||||
if err == nil {
|
||||
exec.Command("blockdev", "--flushbufs", p.partDev).Run()
|
||||
os.Remove(p.mountPath)
|
||||
return true
|
||||
} else {
|
||||
time.Sleep(time.Second * 1)
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -0,0 +1,417 @@
|
||||
package guestfs
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"path"
|
||||
"strings"
|
||||
"syscall"
|
||||
|
||||
"yunion.io/x/jsonutils"
|
||||
"yunion.io/x/log"
|
||||
"yunion.io/x/onecloud/pkg/cloudcommon/fstabutils"
|
||||
"yunion.io/x/onecloud/pkg/cloudcommon/sshkeys"
|
||||
"yunion.io/x/onecloud/pkg/hostman"
|
||||
"yunion.io/x/onecloud/pkg/hostman/options"
|
||||
"yunion.io/x/onecloud/pkg/util/seclib2"
|
||||
"yunion.io/x/pkg/utils"
|
||||
)
|
||||
|
||||
type SLinuxRootFs struct {
|
||||
*SGuestRootFsDriver
|
||||
}
|
||||
|
||||
func NewLinuxRootFs(part *SKVMGuestDiskPartition) IRootFsDriver {
|
||||
return &SAndroidRootFs{SGuestRootFsDriver: NewGuestRootFsDriver(part).(*SGuestRootFsDriver)}
|
||||
}
|
||||
|
||||
func (l *SLinuxRootFs) String() string {
|
||||
return "LinuxRootFs"
|
||||
}
|
||||
|
||||
func (l *SLinuxRootFs) RootSignatures() []string {
|
||||
return []string{"/bin", "/etc", "/boot", "/lib", "/usr"}
|
||||
}
|
||||
|
||||
func (l *SLinuxRootFs) DeployHost(hn, domain string, ips []string) error {
|
||||
var oldHostFile string
|
||||
if l.rootFs.Exists("/etc/hosts", false) {
|
||||
oldhf, err := l.rootFs.FileGetContents("/etc/hosts", false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
oldHostFile = string(oldhf)
|
||||
}
|
||||
hf := make(hostman.HostsFile, 0)
|
||||
hf.Parse(oldHostFile)
|
||||
hf.Add("127.0.0.1", "localhost")
|
||||
for _, ip := range ips {
|
||||
hf.Add(ip, fmt.Sprintf("%s.%s", hn+domain), hn)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (l *SLinuxRootFs) GetLoginAccount() string {
|
||||
var selUsr string
|
||||
if options.HostOptions.LinuxDefaultRootUser && l.rootFs.Exists("/root", false) {
|
||||
selUsr = "root"
|
||||
} else {
|
||||
usrs := l.rootFs.Listdir("/home", false)
|
||||
for _, usr := range usrs {
|
||||
if len(selUsr) == 0 || len(selUsr) > len(usr) {
|
||||
selUsr = usr
|
||||
}
|
||||
}
|
||||
if len(selUsr) > 0 && l.rootFs.Exists("/root", false) {
|
||||
selUsr = "root"
|
||||
}
|
||||
}
|
||||
return selUsr
|
||||
}
|
||||
|
||||
func (l *SLinuxRootFs) ChangeUserPassswd(account, gid, publicKey, password string) string {
|
||||
var secret string
|
||||
if err := l.rootFs.Passwd(account, password, false); err != nil {
|
||||
if len(publicKey) > 0 {
|
||||
secret, _ = seclib2.EncryptBase64(publicKey, password)
|
||||
} else {
|
||||
secret, _ = utils.EncryptAESBase64(gid, password)
|
||||
}
|
||||
} else {
|
||||
log.Errorf("Change uer passwd error: %s", err)
|
||||
}
|
||||
return secret
|
||||
}
|
||||
|
||||
func (l *SLinuxRootFs) DeployPublicKey(selUsr string, pubkeys *sshkeys.SSHKeys) error {
|
||||
var usrDir string
|
||||
if selUsr == "root" {
|
||||
usrDir = "/root"
|
||||
} else {
|
||||
usrDir = path.Join("/home", selUsr)
|
||||
}
|
||||
return l.rootFs.DeployAuthorizedKeys(usrDir, pubkeys, false)
|
||||
}
|
||||
|
||||
func (l *SLinuxRootFs) DeployYunionroot(pubkeys *sshkeys.SSHKeys) error {
|
||||
l.DisableSelinux()
|
||||
l.DisableCloudinit()
|
||||
var yunionroot = "cloudroot"
|
||||
if err := l.rootFs.UserAdd(yunionroot, false); err != nil {
|
||||
return err
|
||||
}
|
||||
err := l.rootFs.DeployAuthorizedKeys(path.Join("/home", yunionroot), pubkeys, false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return l.EnableUserSudo(yunionroot)
|
||||
}
|
||||
|
||||
func (l *SLinuxRootFs) EnableUserSudo(user string) error {
|
||||
var sudoDir = "/etc/sudoers.d"
|
||||
var content = fmt.Sprintf("%s ALL=(ALL) NOPASSWD:ALL\n", user)
|
||||
if l.rootFs.Exists(sudoDir, false) {
|
||||
filepath := path.Join(sudoDir, fmt.Sprintf("90-%s-users", user))
|
||||
err := l.rootFs.FilePutContents(filepath, content, false, false)
|
||||
if err != nil {
|
||||
log.Errorln(err)
|
||||
return err
|
||||
}
|
||||
return l.rootFs.Chmod(filepath, syscall.S_IRUSR|syscall.S_IRGRP, false)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (l *SLinuxRootFs) DisableSelinux() {
|
||||
selinuxConfig := "/etc/selinux/config"
|
||||
content := `# This file controls the state of SELinux on the system.
|
||||
# SELINUX= can take one of these three values:
|
||||
# enforcing - SELinux security policy is enforced.
|
||||
# permissive - SELinux prints warnings instead of enforcing.
|
||||
# disabled - No SELinux policy is loaded.
|
||||
SELINUX=disabled
|
||||
# SELINUXTYPE= can take one of three two values:
|
||||
# targeted - Targeted processes are protected,
|
||||
# minimum - Modification of targeted policy. Only selected processes are protected.
|
||||
# mls - Multi Level Security protection.
|
||||
SELINUXTYPE=targeted
|
||||
`
|
||||
if l.rootFs.Exists(selinuxConfig, false) {
|
||||
l.rootFs.FilePutContents(selinuxConfig, content, false, false)
|
||||
}
|
||||
}
|
||||
|
||||
func (l *SLinuxRootFs) DisableCloudinit() {
|
||||
cloudDir := "/etc/cloud"
|
||||
cloudDisableFile := "/etc/cloud/cloud-init.disabled"
|
||||
if l.rootFs.Exists(cloudDir, false) {
|
||||
l.rootFs.FilePutContents(cloudDisableFile, "", false, false)
|
||||
}
|
||||
}
|
||||
|
||||
func (l *SLinuxRootFs) DeployFstabScripts(disks []jsonutils.JSONObject) error {
|
||||
fstabcont, err := l.rootFs.FileGetContents("/etc/fstab", false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var dataDiskIdx = 0
|
||||
var rec string
|
||||
var modeRwxOwner = syscall.S_IRUSR | syscall.S_IWUSR | syscall.S_IXUSR
|
||||
var fstab = fstabutils.FSTabFile(string(fstabcont))
|
||||
|
||||
for i := 1; i < len(disks); i++ {
|
||||
diskId, err := disks[i].GetString("disk_id")
|
||||
if err != nil {
|
||||
diskId = "None"
|
||||
}
|
||||
dev := fmt.Sprintf("UUID=%s", diskId)
|
||||
if !fstab.IsExists(dev) {
|
||||
fs, err := disks[i].GetString("fs")
|
||||
if fs == "swap" {
|
||||
rec = fmt.Sprintf("%s none %s sw 0 0", dev, fs)
|
||||
} else {
|
||||
mtPath, _ := disks[i].GetString("mountpoint")
|
||||
if len(mtPath) == 0 {
|
||||
mtPath = "/data"
|
||||
if dataDiskIdx > 0 {
|
||||
mtPath += fmt.Sprintf("%d", dataDiskIdx)
|
||||
}
|
||||
dataDiskIdx += 1
|
||||
}
|
||||
rec = fmt.Sprintf("%s %s %s defaults 2 2", dev, mtPath, fs)
|
||||
if !l.rootFs.Exists(mtPath, false) {
|
||||
if err := l.rootFs.Mkdir(mtPath, modeRwxOwner, false); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
fstab.AddFsrec(rec)
|
||||
}
|
||||
}
|
||||
cf := fstab.ToConf()
|
||||
return l.rootFs.FilePutContents("/etc/fstab", cf, false, false)
|
||||
}
|
||||
|
||||
func (l *SLinuxRootFs) DeployNetworkingScripts(nics []jsonutils.JSONObject) error {
|
||||
udevPath := "/etc/udev/rules.d/"
|
||||
if l.rootFs.Exists(udevPath, false) {
|
||||
rules := l.rootFs.Listdir(udevPath, false)
|
||||
for _, rule := range rules {
|
||||
if strings.Index(rule, "persistent-net.rules") > 0 {
|
||||
l.rootFs.Remove(path.Join(udevPath, rule), false)
|
||||
} else if strings.Index(rule, "persistent-cd.rules") > 0 {
|
||||
if err := l.rootFs.FilePutContents(path.Join(udevPath, rule), "", false, false); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
var nicRules string
|
||||
for _, nic := range nics {
|
||||
nicRules += `KERNEL=="eth*", SUBSYSTEM=="net", ACTION=="add", `
|
||||
nicRules += `DRIVERS=="?*", `
|
||||
mac, _ := nic.GetString("mac")
|
||||
nicRules += fmt.Sprintf(`ATTR{address}=="%s", ATTR{type}=="1", `, strings.ToLower(mac))
|
||||
idx, _ := nic.Int("index")
|
||||
nicRules += fmt.Sprintf(`NAME="eth%d"\n`, idx)
|
||||
}
|
||||
if err := l.rootFs.FilePutContents(path.Join(udevPath, "70-persistent-net.rules"), nicRules, false, false); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var usbRules string
|
||||
usbRules = `SUBSYSTEM=="usb", ATTRS{idVendor}=="1d6b", ATTRS{idProduct}=="0001", `
|
||||
usbRules += `RUN+="/bin/sh -c \'echo enabled > /sys$env{DEVPATH}/../power/wakeup\'"\n`
|
||||
if err := l.rootFs.FilePutContents(path.Join(udevPath,
|
||||
"90-usb-tablet-remote-wakeup.rules"), usbRules, false, false); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (l *SLinuxRootFs) DeployStandbyNetworkingScripts(nics, nicsStandby []jsonutils.JSONObject) error {
|
||||
var udevPath = "/etc/udev/rules.d/"
|
||||
var nicRules string
|
||||
for _, nic := range nicsStandby {
|
||||
nicType, _ := nic.GetString("nic_type")
|
||||
if !nic.Contains("nic_type") || nicType != "impi" {
|
||||
nicRules += `KERNEL=="eth*", SUBSYSTEM=="net", ACTION=="add", `
|
||||
nicRules += `DRIVERS=="?*", `
|
||||
mac, _ := nic.GetString("mac")
|
||||
nicRules += fmt.Sprintf(`ATTR{address}=="%s", ATTR{type}=="1", `, strings.ToLower(mac))
|
||||
idx, _ := nic.Int("index")
|
||||
nicRules += fmt.Sprintf(`NAME="eth%d"\n`, idx)
|
||||
}
|
||||
}
|
||||
if err := l.rootFs.FilePutContents(path.Join(udevPath, "70-persistent-net.rules"), nicRules, false, false); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (l *SLinuxRootFs) GetOs() string {
|
||||
return "Linux"
|
||||
}
|
||||
|
||||
func (l *SLinuxRootFs) GetArch() string {
|
||||
if l.rootFs.Exists("/lib64", false) && l.rootFs.Exists("/usr/lib64", false) {
|
||||
return "x86_64"
|
||||
} else {
|
||||
return "x86"
|
||||
}
|
||||
}
|
||||
|
||||
func (l *SLinuxRootFs) PrepareFsForTemplate() {
|
||||
// clean /etc/fstab
|
||||
if l.rootFs.Exists("/etc/fstab", false) {
|
||||
fstabcont, _ := l.rootFs.FileGetContents("/etc/fstab", false)
|
||||
fstab := fstabutils.FSTabFile(string(fstabcont))
|
||||
fstab.RemoveDevices(1)
|
||||
cf := fstab.ToConf()
|
||||
l.rootFs.FilePutContents("/etc/fstab", cf, false, false)
|
||||
}
|
||||
// rm /etc/ssh/*_key.*
|
||||
if l.rootFs.Exists("/etc/ssh", false) {
|
||||
for _, f := range l.rootFs.Listdir("/etc/ssh", false) {
|
||||
if strings.HasSuffix(f, "_key") || strings.HasSuffix(f, "_key.pub") {
|
||||
l.rootFs.Remove("/etc/ssh/"+f, false)
|
||||
}
|
||||
}
|
||||
}
|
||||
// clean cloud-init
|
||||
if l.rootFs.Exists("/var/lib/cloud", false) {
|
||||
l.rootFs.Cleandir("/var/lib/cloud", false, false)
|
||||
}
|
||||
cloudDisableFile := "/etc/cloud/cloud-init.disabled"
|
||||
if l.rootFs.Exists(cloudDisableFile, false) {
|
||||
l.rootFs.Remove(cloudDisableFile, false)
|
||||
}
|
||||
// clean /tmp /var/log /var/cache /var/spool /var/run
|
||||
for _, dir := range []string{"/tmp", "/var/tmp"} {
|
||||
if l.rootFs.Exists(dir, false) {
|
||||
l.rootFs.Cleandir(dir, false, false)
|
||||
}
|
||||
}
|
||||
for _, dir := range []string{"/var/log", "/var/cache", "/usr/local/var/log", "/usr/local/var/cache"} {
|
||||
if l.rootFs.Exists(dir, false) {
|
||||
l.rootFs.Zerofiles(dir, false)
|
||||
}
|
||||
}
|
||||
for _, dir := range []string{"/var/spool", "/var/run", "/run", "/usr/local/var/spool", "/usr/local/var/run"} {
|
||||
if l.rootFs.Exists(dir, false) {
|
||||
l.rootFs.Cleandir(dir, true, true)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type SDebianLikeRootFs struct {
|
||||
*SLinuxRootFs
|
||||
}
|
||||
|
||||
func NewDebianLikeRootFs(part *SKVMGuestDiskPartition) IRootFsDriver {
|
||||
return &SDebianLikeRootFs{SLinuxRootFs: NewLinuxRootFs(part).(*SLinuxRootFs)}
|
||||
}
|
||||
|
||||
type SDebianRootFs struct {
|
||||
*SDebianLikeRootFs
|
||||
}
|
||||
|
||||
func NewDebianRootFs(part *SKVMGuestDiskPartition) IRootFsDriver {
|
||||
return &SDebianRootFs{SDebianLikeRootFs: NewDebianLikeRootFs(part).(*SDebianLikeRootFs)}
|
||||
}
|
||||
|
||||
type SCirrosRootFs struct {
|
||||
*SDebianRootFs
|
||||
}
|
||||
|
||||
func NewCirrosRootFs(part *SKVMGuestDiskPartition) IRootFsDriver {
|
||||
return &SCirrosRootFs{SDebianRootFs: NewDebianRootFs(part).(*SDebianRootFs)}
|
||||
}
|
||||
|
||||
type SCirrosNewRootFs struct {
|
||||
*SDebianRootFs
|
||||
}
|
||||
|
||||
func NewCirrosNewRootFs(part *SKVMGuestDiskPartition) IRootFsDriver {
|
||||
return &SCirrosNewRootFs{SDebianRootFs: NewDebianRootFs(part).(*SDebianRootFs)}
|
||||
}
|
||||
|
||||
type SUbuntuRootFs struct {
|
||||
*SDebianRootFs
|
||||
}
|
||||
|
||||
func NewUbuntuRootFs(part *SKVMGuestDiskPartition) IRootFsDriver {
|
||||
return &SUbuntuRootFs{SDebianRootFs: NewDebianRootFs(part).(*SDebianRootFs)}
|
||||
}
|
||||
|
||||
type SRedhatLikeRootFs struct {
|
||||
*SLinuxRootFs
|
||||
}
|
||||
|
||||
func NewRedhatLikeRootFs(part *SKVMGuestDiskPartition) IRootFsDriver {
|
||||
return &SRedhatLikeRootFs{SLinuxRootFs: NewLinuxRootFs(part).(*SLinuxRootFs)}
|
||||
}
|
||||
|
||||
type SCentosRootFs struct {
|
||||
*SRedhatLikeRootFs
|
||||
}
|
||||
|
||||
func NewCentosRootFs(part *SKVMGuestDiskPartition) IRootFsDriver {
|
||||
return &SCentosRootFs{SRedhatLikeRootFs: NewRedhatLikeRootFs(part).(*SRedhatLikeRootFs)}
|
||||
}
|
||||
|
||||
type SFedoraRootFs struct {
|
||||
*SRedhatLikeRootFs
|
||||
}
|
||||
|
||||
func NewFedoraRootFs(part *SKVMGuestDiskPartition) IRootFsDriver {
|
||||
return &SFedoraRootFs{SRedhatLikeRootFs: NewRedhatLikeRootFs(part).(*SRedhatLikeRootFs)}
|
||||
}
|
||||
|
||||
type SRhelRootFs struct {
|
||||
*SRedhatLikeRootFs
|
||||
}
|
||||
|
||||
func NewRhelRootFs(part *SKVMGuestDiskPartition) IRootFsDriver {
|
||||
return &SRhelRootFs{SRedhatLikeRootFs: NewRedhatLikeRootFs(part).(*SRedhatLikeRootFs)}
|
||||
}
|
||||
|
||||
type SGentooRootFs struct {
|
||||
*SLinuxRootFs
|
||||
}
|
||||
|
||||
func NewGentooRootFs(part *SKVMGuestDiskPartition) IRootFsDriver {
|
||||
return &SGentooRootFs{SLinuxRootFs: NewLinuxRootFs(part).(*SLinuxRootFs)}
|
||||
}
|
||||
|
||||
type SArchLinuxRootFs struct {
|
||||
*SLinuxRootFs
|
||||
}
|
||||
|
||||
func NewArchLinuxRootFs(part *SKVMGuestDiskPartition) IRootFsDriver {
|
||||
return &SArchLinuxRootFs{SLinuxRootFs: NewLinuxRootFs(part).(*SLinuxRootFs)}
|
||||
}
|
||||
|
||||
type SOpenWrtRootFs struct {
|
||||
*SLinuxRootFs
|
||||
}
|
||||
|
||||
func NewOpenWrtRootFs(part *SKVMGuestDiskPartition) IRootFsDriver {
|
||||
return &SOpenWrtRootFs{SLinuxRootFs: NewLinuxRootFs(part).(*SLinuxRootFs)}
|
||||
}
|
||||
|
||||
type SCoreOsRootFs struct {
|
||||
*SGuestRootFsDriver
|
||||
}
|
||||
|
||||
func NewCoreOsRootFs(part *SKVMGuestDiskPartition) IRootFsDriver {
|
||||
return &SCoreOsRootFs{SGuestRootFsDriver: NewGuestRootFsDriver(part).(*SGuestRootFsDriver)}
|
||||
}
|
||||
|
||||
func init() {
|
||||
linuxFsDrivers := []newRootFsDriverFunc{
|
||||
NewLinuxRootFs, NewDebianLikeRootFs, NewDebianRootFs, NewCirrosRootFs, NewCirrosNewRootFs,
|
||||
NewUbuntuRootFs, NewRedhatLikeRootFs, NewCentosRootFs, NewFedoraRootFs, NewRhelRootFs,
|
||||
NewGentooRootFs, NewArchLinuxRootFs, NewOpenWrtRootFs, NewCoreOsRootFs,
|
||||
}
|
||||
rootfsDrivers = append(rootfsDrivers, linuxFsDrivers...)
|
||||
}
|
||||
@@ -0,0 +1,323 @@
|
||||
package guestfs
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"math/rand"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path"
|
||||
"strings"
|
||||
"syscall"
|
||||
|
||||
"yunion.io/x/log"
|
||||
"yunion.io/x/onecloud/pkg/cloudcommon/sshkeys"
|
||||
"yunion.io/x/onecloud/pkg/hostman"
|
||||
)
|
||||
|
||||
type SLocalGuestFS struct {
|
||||
mountPath string
|
||||
readOnly bool
|
||||
}
|
||||
|
||||
func (f *SLocalGuestFS) IsReadonly() bool {
|
||||
log.Infof("Test if read-only fs ...")
|
||||
var filename = fmt.Sprint("./%f", rand.Float32())
|
||||
if err := f.FilePutContents(filename, fmt.Sprint("%f", rand.Float32()), false, false); err == nil {
|
||||
f.Remove(filename, false)
|
||||
return false
|
||||
} else {
|
||||
log.Errorf("File system is readonly: %s", err)
|
||||
f.readOnly = true
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
func (f *SLocalGuestFS) GetReadonly() bool {
|
||||
return f.readOnly
|
||||
}
|
||||
|
||||
func (f *SLocalGuestFS) getLocalPath(sPath string, caseInsensitive bool) string {
|
||||
var fullPath = f.mountPath
|
||||
pathSegs := strings.Split(sPath, "/")
|
||||
for _, seg := range pathSegs {
|
||||
if len(seg) > 0 {
|
||||
var realSeg string
|
||||
files, _ := ioutil.ReadDir(fullPath)
|
||||
for _, file := range files {
|
||||
var f = file.Name()
|
||||
if f == seg || (caseInsensitive && (strings.ToLower(f)) == strings.ToLower(seg)) ||
|
||||
(seg[len(seg)-1] == '*' && strings.HasPrefix(f, seg[:len(seg)-1])) ||
|
||||
(caseInsensitive && strings.HasPrefix(strings.ToLower(f),
|
||||
strings.ToLower(seg[:len(seg)]))) {
|
||||
realSeg = f
|
||||
break
|
||||
}
|
||||
}
|
||||
if len(realSeg) > 0 {
|
||||
fullPath = path.Join(fullPath, realSeg)
|
||||
} else {
|
||||
return ""
|
||||
}
|
||||
}
|
||||
}
|
||||
return fullPath
|
||||
}
|
||||
|
||||
func (f *SLocalGuestFS) Remove(path string, caseInsensitive bool) {
|
||||
path = f.getLocalPath(path, caseInsensitive)
|
||||
if len(path) > 0 {
|
||||
os.Remove(path)
|
||||
}
|
||||
}
|
||||
|
||||
func (f *SLocalGuestFS) Mkdir(sPath string, mode int, caseInsensitive bool) error {
|
||||
segs := strings.Split(sPath, "/")
|
||||
sPath = ""
|
||||
pPath := f.getLocalPath("/", caseInsensitive)
|
||||
for _, s := range segs {
|
||||
if len(s) > 0 {
|
||||
sPath = path.Join(sPath, s)
|
||||
vPath := f.getLocalPath(sPath, caseInsensitive)
|
||||
if len(vPath) > 0 {
|
||||
if err := os.Mkdir(path.Join(pPath, s), os.FileMode(mode)); err != nil {
|
||||
return err
|
||||
}
|
||||
pPath = f.getLocalPath(sPath, caseInsensitive)
|
||||
} else {
|
||||
pPath = vPath
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *SLocalGuestFS) Listdir(sPath string, caseInsensitive bool) []string {
|
||||
sPath = f.getLocalPath(sPath, caseInsensitive)
|
||||
if len(sPath) > 0 {
|
||||
files, err := ioutil.ReadDir(sPath)
|
||||
if err != nil {
|
||||
log.Errorln(err)
|
||||
return nil
|
||||
}
|
||||
var res = make([]string, 0)
|
||||
for _, file := range files {
|
||||
res = append(res, file.Name())
|
||||
}
|
||||
return res
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *SLocalGuestFS) Cleandir(dir string, keepdir, caseInsensitive bool) error {
|
||||
sPath := f.getLocalPath(dir, caseInsensitive)
|
||||
if len(sPath) > 0 {
|
||||
return hostman.Cleandir(sPath, keepdir)
|
||||
}
|
||||
return fmt.Errorf("No such file %s", sPath)
|
||||
}
|
||||
|
||||
// TODO
|
||||
func (f *SLocalGuestFS) Zerofiles(dir string, caseInsensitive bool) error {
|
||||
sPath := f.getLocalPath(dir, caseInsensitive)
|
||||
if len(sPath) > 0 {
|
||||
//写到这里了。。。
|
||||
return hostman.Zerofiles(sPath)
|
||||
}
|
||||
return fmt.Errorf("No such file %s", sPath)
|
||||
}
|
||||
|
||||
// TODO: test
|
||||
func (f *SLocalGuestFS) Passwd(account, password string, caseInsensitive bool) error {
|
||||
var proc = exec.Command("chroot", f.mountPath, "passwd", account)
|
||||
stdin, err := proc.StdinPipe()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer stdin.Close()
|
||||
|
||||
outb, err := proc.StdoutPipe()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer outb.Close()
|
||||
|
||||
errb, err := proc.StderrPipe()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer errb.Close()
|
||||
|
||||
if err := proc.Start(); err != nil {
|
||||
return err
|
||||
}
|
||||
io.WriteString(stdin, fmt.Sprintf("%s\n", password))
|
||||
io.WriteString(stdin, fmt.Sprintf("%s\n", password))
|
||||
stdoutPut, err := ioutil.ReadAll(outb)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
stderrOutPut, err := ioutil.ReadAll(errb)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
log.Infof("Passwd %s %s", stdoutPut, stderrOutPut)
|
||||
return proc.Wait()
|
||||
}
|
||||
|
||||
func (f *SLocalGuestFS) Stat(usrDir string, caseInsensitive bool) os.FileInfo {
|
||||
sPath := f.getLocalPath(usrDir, caseInsensitive)
|
||||
if len(sPath) > 0 {
|
||||
fileInfo, err := os.Stat(sPath)
|
||||
if err != nil {
|
||||
log.Errorln(err)
|
||||
}
|
||||
return fileInfo
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *SLocalGuestFS) Exists(sPath string, caseInsensitive bool) bool {
|
||||
sPath = f.getLocalPath(sPath, caseInsensitive)
|
||||
if len(sPath) > 0 {
|
||||
if _, err := os.Stat(sPath); !os.IsNotExist(err) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (f *SLocalGuestFS) Chown(sPath string, uid, gid int, caseInsensitive bool) error {
|
||||
sPath = f.getLocalPath(sPath, caseInsensitive)
|
||||
if len(sPath) > 0 {
|
||||
return os.Chown(sPath, uid, gid)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *SLocalGuestFS) Chmod(sPath string, mode uint32, caseInsensitive bool) error {
|
||||
sPath = f.getLocalPath(sPath, caseInsensitive)
|
||||
if len(sPath) > 0 {
|
||||
return os.Chmod(sPath, os.FileMode(mode))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *SLocalGuestFS) UserAdd(user string, caseInsensitive bool) error {
|
||||
output, err := exec.Command("chroot", f.mountPath, "useradd", user).Output()
|
||||
if err != nil {
|
||||
log.Errorf("Useradd fail: %s", err)
|
||||
return err
|
||||
} else {
|
||||
log.Infof("Useradd: %s", output)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *SLocalGuestFS) MergeAuthorizedKeys(oldKeys string, pubkeys *sshkeys.SSHKeys) string {
|
||||
var allkeys = make(map[string]string, 0)
|
||||
if len(oldKeys) > 0 {
|
||||
for _, line := range strings.Split(oldKeys, "\n") {
|
||||
line = strings.TrimSpace(line)
|
||||
dat := strings.Split(line, " ")
|
||||
if len(dat) > 1 {
|
||||
if _, ok := allkeys[dat[1]]; !ok {
|
||||
allkeys[dat[1]] = line
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if len(pubkeys.DeletePublicKey) > 0 {
|
||||
dat := strings.Split(pubkeys.DeletePublicKey, " ")
|
||||
if len(dat) > 1 {
|
||||
if _, ok := allkeys[dat[1]]; ok {
|
||||
delete(allkeys, dat[1])
|
||||
}
|
||||
}
|
||||
}
|
||||
for _, k := range []string{pubkeys.PublicKey, pubkeys.AdminPublicKey, pubkeys.ProjectPublicKey} {
|
||||
if len(k) > 0 {
|
||||
k = strings.TrimSpace(k)
|
||||
dat := strings.Split(k, " ")
|
||||
if len(dat) > 1 {
|
||||
if _, ok := allkeys[dat[1]]; !ok {
|
||||
allkeys[dat[1]] = k
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
var keys = make([]string, len(allkeys))
|
||||
for key, _ := range allkeys {
|
||||
keys = append(keys, key)
|
||||
}
|
||||
return strings.Join(keys, "\n")
|
||||
}
|
||||
|
||||
func (f *SLocalGuestFS) DeployAuthorizedKeys(usrDir string, pubkeys *sshkeys.SSHKeys, replace bool) error {
|
||||
usrStat := f.Stat(usrDir, false)
|
||||
if usrStat != nil {
|
||||
sshDir := path.Join(usrDir, ".ssh")
|
||||
authFile := path.Join(sshDir, "authorized_keys")
|
||||
modeRwxOwner := syscall.S_IRUSR | syscall.S_IWUSR | syscall.S_IXUSR
|
||||
modeRwOwner := syscall.S_IRUSR | syscall.S_IWUSR
|
||||
fStat := usrStat.Sys().(*syscall.Stat_t)
|
||||
if !f.Exists(sshDir, false) {
|
||||
err := f.Mkdir(sshDir, modeRwxOwner, false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = f.Chown(sshDir, int(fStat.Uid), int(fStat.Gid), false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
var oldKeys = ""
|
||||
if !replace {
|
||||
bOldKeys, _ := f.FileGetContents(authFile, false)
|
||||
oldKeys = string(bOldKeys)
|
||||
}
|
||||
newKeys := f.MergeAuthorizedKeys(oldKeys, pubkeys)
|
||||
err := f.FilePutContents(authFile, newKeys, false, false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = f.Chown(authFile, int(fStat.Uid), int(fStat.Gid), false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return f.Chmod(authFile, uint32(modeRwOwner), false)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *SLocalGuestFS) FileGetContents(sPath string, caseInsensitive bool) ([]byte, error) {
|
||||
sPath = f.getLocalPath(sPath, caseInsensitive)
|
||||
if len(sPath) > 0 {
|
||||
return ioutil.ReadFile(sPath)
|
||||
}
|
||||
return nil, fmt.Errorf("Cann't find local path")
|
||||
}
|
||||
|
||||
func (f *SLocalGuestFS) FilePutContents(sPath, content string, modAppend, caseInsensitive bool) error {
|
||||
sFilePath := f.getLocalPath(sPath, caseInsensitive)
|
||||
if len(sFilePath) > 0 {
|
||||
sPath = sFilePath
|
||||
} else {
|
||||
dirPath := f.getLocalPath(path.Dir(sPath), caseInsensitive)
|
||||
if len(dirPath) > 0 {
|
||||
sPath = path.Join(dirPath, path.Base(sPath))
|
||||
}
|
||||
}
|
||||
if len(sPath) > 0 {
|
||||
return hostman.FilePutContents(sPath, content, modAppend)
|
||||
} else {
|
||||
return fmt.Errorf("Cann't put content")
|
||||
}
|
||||
}
|
||||
|
||||
func NewLocalGuestFS(mountPath string) *SLocalGuestFS {
|
||||
var ret = new(SLocalGuestFS)
|
||||
ret.mountPath = mountPath
|
||||
return ret
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package guestfs
|
||||
|
||||
type SMacOSRootFs struct {
|
||||
*SGuestRootFsDriver
|
||||
}
|
||||
|
||||
func NewMacOSRootFs(part *SKVMGuestDiskPartition) IRootFsDriver {
|
||||
return &SMacOSRootFs{SGuestRootFsDriver: NewGuestRootFsDriver(part).(*SGuestRootFsDriver)}
|
||||
}
|
||||
|
||||
func init() {
|
||||
rootfsDrivers = append(rootfsDrivers, NewMacOSRootFs)
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package guestfs
|
||||
|
||||
type SWindowsRootFs struct {
|
||||
*SGuestRootFsDriver
|
||||
}
|
||||
|
||||
func NewWindowsRootFs(part *SKVMGuestDiskPartition) IRootFsDriver {
|
||||
return &SWindowsRootFs{SGuestRootFsDriver: NewGuestRootFsDriver(part).(*SGuestRootFsDriver)}
|
||||
}
|
||||
|
||||
func init() {
|
||||
rootfsDrivers = append(rootfsDrivers, NewWindowsRootFs)
|
||||
}
|
||||
@@ -0,0 +1,488 @@
|
||||
package guestfs
|
||||
|
||||
const WinScriptChangePassword = `
|
||||
|
||||
$username = $args[0]
|
||||
$passwd = $args[1]
|
||||
$loghash = $args[2]
|
||||
$logpath = $args[3]
|
||||
Function ChangePassword($u, $p) {
|
||||
$admin = [adsi]("WinNT://./$($u), user")
|
||||
$succ = 0
|
||||
$tried = 0
|
||||
$max_tries = 10
|
||||
while (($succ -eq 0) -and ($tried -lt $max_tries)) {
|
||||
Try {
|
||||
$admin.psbase.invoke("SetPassword", $p)
|
||||
$admin.psbase.CommitChanges()
|
||||
$succ = 1
|
||||
} Catch {
|
||||
Start-Sleep -s 1
|
||||
}
|
||||
$tried = $tried + 1
|
||||
}
|
||||
}
|
||||
if ($username -and $passwd) {
|
||||
if ($logpath) {
|
||||
"starting $loghash" | Out-File $logpath -Append -Encoding Default
|
||||
ChangePassword $username $passwd 2>&1 | Out-File $logpath -Append -Encoding Default
|
||||
} else {
|
||||
ChangePassword $username $passwd 2>&1 | Out-Null
|
||||
}
|
||||
}
|
||||
|
||||
`
|
||||
|
||||
const WinScriptMountDisk = `
|
||||
|
||||
var MTW_GLOBAL_FSO = new ActiveXObject('Scripting.FileSystemObject');
|
||||
var MTW_SCRIPT_PATH = mtw_gen_script_path();
|
||||
var MTW_DEBUG_STREAM = null;
|
||||
|
||||
function mtw_create_shell() {
|
||||
return new ActiveXObject('WScript.Shell');
|
||||
}
|
||||
|
||||
function mtw_gen_script_path() {
|
||||
var fso = MTW_GLOBAL_FSO;
|
||||
var folder = fso.GetSpecialFolder(2); // TemporaryFolder
|
||||
var folder_path = folder + '';
|
||||
var script_name = [];
|
||||
if (!/[\\\/]$/.test(folder_path)) {
|
||||
folder_path += '\\';
|
||||
}
|
||||
for (var i = 0; i < 5; i++) {
|
||||
script_name.push(mtw_gen_random_str(6));
|
||||
}
|
||||
return folder_path + script_name.join('-');
|
||||
}
|
||||
|
||||
function mtw_gen_random_str(length) {
|
||||
var offset, result = [];
|
||||
var charcode_base = 'A'.charCodeAt(0);
|
||||
for (var i = 0; i < length; i++) {
|
||||
offset = Math.floor(Math.random() * 26);
|
||||
result.push(String.fromCharCode(charcode_base + offset));
|
||||
}
|
||||
return result.join('');
|
||||
}
|
||||
|
||||
function mtw_prepare_debug(file_path) {
|
||||
var fso = new ActiveXObject('Scripting.FileSystemObject');
|
||||
var stream = fso.OpenTextFile(file_path, 8, true); // 8: ForAppending
|
||||
|
||||
stream.WriteLine('');
|
||||
stream.WriteLine('================ ' + new Date() + ' ================');
|
||||
stream.WriteLine('');
|
||||
|
||||
MTW_DEBUG_STREAM = stream;
|
||||
}
|
||||
|
||||
function mtw_append_debug(cmd_lines, result_lines) {
|
||||
var i, len, stream = MTW_DEBUG_STREAM;
|
||||
|
||||
if (!stream) return;
|
||||
|
||||
stream.WriteLine('');
|
||||
stream.WriteLine('---------- script:');
|
||||
for (i = 0, len = cmd_lines.length; i < len; i++) {
|
||||
stream.WriteLine(cmd_lines[i]);
|
||||
}
|
||||
|
||||
stream.WriteLine('---------- result:');
|
||||
for (i = 0, len = result_lines.length; i < len; i++) {
|
||||
stream.WriteLine(result_lines[i]);
|
||||
}
|
||||
}
|
||||
|
||||
function mtw_execute_diskpart(cmd_lines) {
|
||||
var result_lines = [];
|
||||
var fso = MTW_GLOBAL_FSO;
|
||||
var stream, shell, exec_cmd;
|
||||
|
||||
cmd_lines.push('exit');
|
||||
stream = fso.CreateTextFile(MTW_SCRIPT_PATH, true);
|
||||
for (var i = 0, len = cmd_lines.length; i < len; i++) {
|
||||
stream.WriteLine(cmd_lines[i]);
|
||||
}
|
||||
stream.close();
|
||||
|
||||
shell = mtw_create_shell();
|
||||
exec_cmd = shell.Exec('diskpart /s ' + MTW_SCRIPT_PATH);
|
||||
while (exec_cmd.Status == 0) {
|
||||
WScript.Sleep(100);
|
||||
}
|
||||
stream = exec_cmd.StdOut;
|
||||
while (!stream.AtEndOfStream) {
|
||||
result_lines.push(stream.ReadLine());
|
||||
}
|
||||
fso.DeleteFile(MTW_SCRIPT_PATH);
|
||||
|
||||
mtw_append_debug(cmd_lines, result_lines);
|
||||
|
||||
return result_lines;
|
||||
}
|
||||
|
||||
function mtw_get_disk_list() {
|
||||
var result_lines, line, match, disk_no;
|
||||
var disk_list = [];
|
||||
|
||||
result_lines = mtw_execute_diskpart(['list disk']);
|
||||
for (var i = 0, len = result_lines.length; i < len; i++) {
|
||||
line = result_lines[i];
|
||||
/*
|
||||
Disk ### Status Size Free Dyn Gpt
|
||||
-------- ------------- ------- ------- --- ---
|
||||
Disk 0 Online 20 GB 0 B
|
||||
Disk 1 Offline 10 GB 0 B
|
||||
*/
|
||||
match = line.match(/\s([1-9])\s\D+\s[1-9]\d*\s+[GMK]B\s+\d+\s+[GMK]?B/);
|
||||
if (match) {
|
||||
disk_no = match[1];
|
||||
disk_list.push({
|
||||
'disk_no': disk_no,
|
||||
'partition_list': mtw_get_partition_list(disk_no)
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return disk_list;
|
||||
}
|
||||
|
||||
function mtw_get_partition_list(disk_no) {
|
||||
var result_lines, line, match, partition_no;
|
||||
var partition_list = [];
|
||||
|
||||
result_lines = mtw_execute_diskpart([
|
||||
'select disk=' + disk_no,
|
||||
'list partition'
|
||||
]);
|
||||
for (var i = 0, len = result_lines.length; i < len; i++) {
|
||||
line = result_lines[i];
|
||||
/*
|
||||
Partition ### Type Size Offset
|
||||
------------- ---------------- ------- -------
|
||||
Partition 1 Primary 9 GB 1024 KB
|
||||
*/
|
||||
match = line.match(/\s(\d)\s\D+\s[1-9]\d*\s+[GMK]B\s+\d+\s+[GMK]?B/);
|
||||
if (match) {
|
||||
partition_no = match[1];
|
||||
partition_list.push({
|
||||
'partition_no': partition_no,
|
||||
'partition_type': mtw_get_partition_type(disk_no, partition_no)
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return partition_list;
|
||||
}
|
||||
|
||||
var MTW_PARTITION_TYPE_NODATA = 'nodata';
|
||||
var MTW_PARTITION_TYPE_INVALID = 'invalid';
|
||||
function mtw_get_partition_type(disk_no, partition_no) {
|
||||
var result_lines, line, match;
|
||||
var possible_type_list = [];
|
||||
|
||||
result_lines = mtw_execute_diskpart([
|
||||
'select disk=' + disk_no,
|
||||
'select partition=' + partition_no,
|
||||
'detail partition'
|
||||
]);
|
||||
for (var i = 0, len = result_lines.length; i < len; i++) {
|
||||
line = result_lines[i];
|
||||
/*
|
||||
Partition 1
|
||||
Type : 06
|
||||
Hidden: No
|
||||
Active: No
|
||||
Offset in Bytes: 1048576
|
||||
|
||||
Volume ### Ltr Label Fs Type Size Status Info
|
||||
---------- --- ----------- ----- ---------- ------- --------- --------
|
||||
* Volume 3 RAW Partition 9 GB Healthy
|
||||
*/
|
||||
match = line.match(/:\s*([0-9a-f]{2})\b/i);
|
||||
if (match) {
|
||||
possible_type_list.push(match[1]);
|
||||
}
|
||||
}
|
||||
|
||||
switch (possible_type_list.length) {
|
||||
case 0:
|
||||
return MTW_PARTITION_TYPE_NODATA;
|
||||
case 1:
|
||||
return possible_type_list[0];
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return MTW_PARTITION_TYPE_INVALID;
|
||||
}
|
||||
|
||||
function mtw_get_volume_list(disk_no) {
|
||||
var result_lines, line, match;
|
||||
var sep_line_exist = false;
|
||||
var volume_list = [];
|
||||
|
||||
result_lines = mtw_execute_diskpart([
|
||||
'select disk=' + disk_no,
|
||||
'detail disk'
|
||||
]);
|
||||
for (var i = 0, len = result_lines.length; i < len; i++) {
|
||||
line = result_lines[i];
|
||||
/*
|
||||
Red Hat VirtIO SCSI Disk Device
|
||||
Disk ID: 0004B605
|
||||
Type : SCSI
|
||||
Status : Online
|
||||
Path : 0
|
||||
Target : 0
|
||||
LUN ID : 0
|
||||
Location Path : PCIROOT(0)#PCI(0600)#SCSI(P00T00L00)
|
||||
Current Read-only State : No
|
||||
Read-only : No
|
||||
Boot Disk : No
|
||||
Pagefile Disk : No
|
||||
Hibernation File Disk : No
|
||||
Crashdump Disk : No
|
||||
Clustered Disk : No
|
||||
|
||||
Volume ### Ltr Label Fs Type Size Status Info
|
||||
---------- --- ----------- ----- ---------- ------- --------- --------
|
||||
Volume 3 RAW Partition 9 GB Healthy
|
||||
*/
|
||||
if (!sep_line_exist) {
|
||||
match = line.match(/-+\s+-+\s+-+\s+-+/);
|
||||
if (match) {
|
||||
sep_line_exist = true;
|
||||
}
|
||||
} else {
|
||||
match = line.match(/\s(\d)\s.+\s\d+\s+[GMK]?B/);
|
||||
if (match) {
|
||||
volume_list.push({'volume_no': match[1]});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return volume_list;
|
||||
}
|
||||
|
||||
function mtw_assign_volume_letter(volume_no_set, letter_offset) {
|
||||
var result_lines, line, match, i, len;
|
||||
var volume_no, letter, charcode, charcode_max, letter_set;
|
||||
var volume_map = {}, volume_shift_list = [], cmd_lines = [];
|
||||
|
||||
result_lines = mtw_execute_diskpart(['list volume']);
|
||||
for (i = 0, len = result_lines.length; i < len; i++) {
|
||||
line = result_lines[i];
|
||||
/*
|
||||
Volume ### Ltr Label Fs Type Size Status Info
|
||||
---------- --- ----------- ----- ---------- ------- --------- --------
|
||||
Volume 0 D CD-ROM 0 B No Media
|
||||
Volume 1 ???? NTFS Partition 100 MB Healthy System
|
||||
Volume 2 C NTFS Partition 19 GB Healthy Boot
|
||||
Volume 3 RAW Partition 9 GB Healthy
|
||||
*/
|
||||
match = line.match(/\s(\d)\s+([D-Z])\s.+\d+\s+[GMK]?B/i);
|
||||
if (match) {
|
||||
volume_no = match[1];
|
||||
letter = match[2].toUpperCase();
|
||||
if (volume_map.hasOwnProperty(letter)) {
|
||||
return false;
|
||||
}
|
||||
volume_map[letter] = volume_no;
|
||||
}
|
||||
}
|
||||
|
||||
charcode = 'D'.charCodeAt(0);
|
||||
charcode += letter_offset;
|
||||
letter_set = String.fromCharCode(charcode);
|
||||
if (volume_map.hasOwnProperty(letter_set) && volume_map[letter_set] == volume_no_set) {
|
||||
return true;
|
||||
}
|
||||
charcode_max = 'Z'.charCodeAt(0);
|
||||
while (true) {
|
||||
letter = String.fromCharCode(charcode);
|
||||
if (!volume_map.hasOwnProperty(letter)) {
|
||||
break;
|
||||
}
|
||||
if (charcode >= charcode_max) {
|
||||
return false;
|
||||
}
|
||||
volume_shift_list.push({
|
||||
'volume_no': volume_map[letter],
|
||||
'charcode_next': charcode + 1
|
||||
});
|
||||
charcode++;
|
||||
}
|
||||
if (volume_shift_list.length > 0) {
|
||||
volume_shift_list.sort(function(a, b) {
|
||||
return b.charcode_next - a.charcode_next;
|
||||
});
|
||||
for (i = 0, len = volume_shift_list.length; i < len; i++) {
|
||||
volume_no = volume_shift_list[i].volume_no;
|
||||
charcode = volume_shift_list[i].charcode_next;
|
||||
cmd_lines.push(
|
||||
'select volume=' + volume_no,
|
||||
'assign letter=' + String.fromCharCode(charcode)
|
||||
);
|
||||
}
|
||||
}
|
||||
cmd_lines.push(
|
||||
'select volume=' + volume_no_set,
|
||||
'assign letter=' + letter_set
|
||||
);
|
||||
mtw_execute_diskpart(cmd_lines);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
function mtw_wait_loop(total_ms, step_ms, callback) {
|
||||
while (true) {
|
||||
if (callback()) {
|
||||
break;
|
||||
}
|
||||
if (total_ms < step_ms) {
|
||||
break;
|
||||
}
|
||||
total_ms -= step_ms;
|
||||
WScript.Sleep(step_ms);
|
||||
}
|
||||
}
|
||||
|
||||
function mtw_get_disk_list_wait() {
|
||||
var disk_list_ret = [];
|
||||
|
||||
/* http://support.microsoft.com/kb/870912 */
|
||||
mtw_wait_loop(5000, 500, function() {
|
||||
var i, j, disk_list, disk, partition;
|
||||
disk_list = mtw_get_disk_list();
|
||||
for (i = 0; i < disk_list.length; i++) {
|
||||
disk = disk_list[i];
|
||||
for (j = 0; j < disk.partition_list.length; j++) {
|
||||
partition = disk.partition_list[j];
|
||||
if (partition.partition_type == MTW_PARTITION_TYPE_NODATA) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
disk_list_ret = disk_list;
|
||||
return true;
|
||||
});
|
||||
|
||||
return disk_list_ret;
|
||||
}
|
||||
|
||||
function mtw_get_volume_list_wait(disk_no) {
|
||||
var volume_list_ret = [];
|
||||
|
||||
mtw_wait_loop(5000, 500, function() {
|
||||
var volume_list = mtw_get_volume_list(disk_no);
|
||||
if (volume_list.length > 0) {
|
||||
volume_list_ret = volume_list;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
|
||||
return volume_list_ret;
|
||||
}
|
||||
|
||||
function mtw_mount_disk() {
|
||||
var disk_list, disk_list_mounted, disk, partition, volume_list;
|
||||
var do_mount, do_create, do_delete;
|
||||
var cmd_lines, letter_offset = 0;
|
||||
|
||||
disk_list = mtw_get_disk_list_wait();
|
||||
disk_list_mounted = [];
|
||||
for (var i = 0, len = disk_list.length; i < len; i++) {
|
||||
disk = disk_list[i];
|
||||
partition = null;
|
||||
do_mount = do_create = do_delete = false;
|
||||
if (disk.partition_list.length == 0) {
|
||||
do_mount = true;
|
||||
do_create = true;
|
||||
} else if (disk.partition_list.length == 1) {
|
||||
partition = disk.partition_list[0];
|
||||
switch (partition.partition_type) {
|
||||
case '06': // DOS 3.31+ 16-bit FAT (over 32M)
|
||||
case '07': // Windows NT NTFS
|
||||
do_mount = true;
|
||||
break;
|
||||
case '83': // Linux native partition
|
||||
do_mount = true;
|
||||
do_delete = true;
|
||||
do_create = true;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!do_mount) {
|
||||
continue;
|
||||
}
|
||||
cmd_lines = [
|
||||
'select disk=' + disk.disk_no,
|
||||
'online disk',
|
||||
'attributes disk clear readonly'
|
||||
];
|
||||
mtw_execute_diskpart(cmd_lines);
|
||||
if (do_create) {
|
||||
cmd_lines = ['select disk=' + disk.disk_no];
|
||||
if (partition && do_delete) {
|
||||
cmd_lines.push(
|
||||
'select partition=' + partition.partition_no,
|
||||
'delete partition'
|
||||
);
|
||||
}
|
||||
cmd_lines.push('create partition primary');
|
||||
mtw_execute_diskpart(cmd_lines);
|
||||
}
|
||||
disk_list_mounted.push(disk);
|
||||
}
|
||||
|
||||
for (i = 0, len = disk_list_mounted.length; i < len; i++) {
|
||||
disk = disk_list_mounted[i];
|
||||
if (i == 0) {
|
||||
volume_list = mtw_get_volume_list_wait(disk.disk_no);
|
||||
} else {
|
||||
volume_list = mtw_get_volume_list(disk.disk_no);
|
||||
}
|
||||
if (volume_list.length == 1) {
|
||||
mtw_assign_volume_letter(volume_list[0].volume_no, letter_offset);
|
||||
letter_offset += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function mtw_main() {
|
||||
var exec_helper, args = WScript.Arguments, debug_path = '';
|
||||
|
||||
for (var i = 0, len = args.length; i < len; i++) {
|
||||
if (args(i) == '--debug') {
|
||||
if (i < len) {
|
||||
i += 1;
|
||||
debug_path = args(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (debug_path) {
|
||||
mtw_prepare_debug(debug_path);
|
||||
}
|
||||
|
||||
/* http://support.microsoft.com/kb/937252 */
|
||||
exec_helper = mtw_create_shell().Exec('diskpart');
|
||||
try {
|
||||
mtw_mount_disk();
|
||||
} catch (e) {
|
||||
// nothing
|
||||
}
|
||||
exec_helper.StdIn.WriteLine('exit');
|
||||
while (exec_helper.Status == 0) {
|
||||
WScript.Sleep(100);
|
||||
}
|
||||
}
|
||||
|
||||
mtw_main();
|
||||
|
||||
`
|
||||
@@ -210,7 +210,7 @@ func (m *SGuestManager) DoDeploy(ctx context.Context, sid string, body jsonutils
|
||||
}
|
||||
// TODO
|
||||
publicKey := sshkeys.GetKeys(body)
|
||||
deploys, _ := body.Get("deploys")
|
||||
deploys, _ := body.GetArray("deploys")
|
||||
password, _ := body.GetString("password")
|
||||
resetPassword := jsonutils.QueryBoolean(body, "reset_password", false)
|
||||
if resetPassword && len(password) == 0 {
|
||||
|
||||
@@ -70,7 +70,7 @@ func (d *SBaseDisk) DeployGuestFs(
|
||||
log.Infof("Kvm Disk Connect Success !!")
|
||||
if root := kvmDisk.Mount(); root != nil {
|
||||
defer kvmDisk.Umount(root)
|
||||
return root.DeployGuestFs(guestDesc, deployInfo)
|
||||
return root.DeployGuestFs(root, guestDesc, deployInfo)
|
||||
}
|
||||
}
|
||||
return nil, fmt.Errorf("Kvm disk connect or mount error")
|
||||
|
||||
+62
-2
@@ -7,6 +7,7 @@ import (
|
||||
"os"
|
||||
"os/exec"
|
||||
"path"
|
||||
"regexp"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
@@ -31,7 +32,37 @@ func CommandWithTimeout(timeout int, cmds ...string) *exec.Cmd {
|
||||
|
||||
// file utils
|
||||
|
||||
func FilePutContents(filename string, context string, modAppend bool) error {
|
||||
// TODO: test
|
||||
func Cleandir(sPath string, keepdir bool) error {
|
||||
if f, _ := os.Lstat(sPath); f == nil || f.Mode()&os.ModeSymlink == os.ModeSymlink {
|
||||
return nil
|
||||
}
|
||||
files, _ := ioutil.ReadDir(sPath)
|
||||
for _, file := range files {
|
||||
fp := path.Join(sPath, file.Name())
|
||||
if f, _ := os.Lstat(fp); f.Mode()&os.ModeSymlink == os.ModeSymlink {
|
||||
if !keepdir {
|
||||
if err := os.Remove(fp); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
} else if f.IsDir() {
|
||||
Cleandir(fp, keepdir)
|
||||
if !keepdir {
|
||||
if err := os.Remove(fp); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if err := os.Remove(fp); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func FilePutContents(filename string, content string, modAppend bool) error {
|
||||
var mode = os.O_WRONLY | os.O_CREATE
|
||||
if modAppend {
|
||||
mode = mode | os.O_APPEND
|
||||
@@ -41,7 +72,7 @@ func FilePutContents(filename string, context string, modAppend bool) error {
|
||||
return err
|
||||
}
|
||||
defer fd.Close()
|
||||
_, err = fd.WriteString(context)
|
||||
_, err = fd.WriteString(content)
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -157,3 +188,32 @@ func CleanFailedMountpoints() {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type HostsFile map[string][]string
|
||||
|
||||
func (hf HostsFile) Parse(content string) {
|
||||
lines := strings.Split(content, "\n")
|
||||
for _, line := range lines {
|
||||
data := regexp.MustCompile(`\s+`).Split(line, -1)
|
||||
for len(data) > 0 && data[len(data)-1] == "" {
|
||||
data = data[:len(data)-1]
|
||||
}
|
||||
if len(data) > 1 {
|
||||
hf[data[0]] = data[1:]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (hf HostsFile) Add(name string, value ...string) {
|
||||
hf[name] = value
|
||||
}
|
||||
|
||||
func (hf HostsFile) String() string {
|
||||
var ret = ""
|
||||
for k, v := range hf {
|
||||
if len(v) > 0 {
|
||||
ret += fmt.Sprintf("%s\t%s\n", k, strings.Join(v, "\t"))
|
||||
}
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user