2018-12-17 ~ 81-21-8102

This commit is contained in:
wanyaoqi
2019-01-22 16:56:47 +08:00
committed by Zexi Li
parent c22561e9f5
commit fc7a9f3728
13 changed files with 1761 additions and 68 deletions
+105
View File
@@ -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()
*/
+13
View File
@@ -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
View File
@@ -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
}
+13
View File
@@ -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)
}
+48 -2
View File
@@ -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
}
+417
View File
@@ -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...)
}
+323
View File
@@ -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
}
+13
View File
@@ -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)
}
+13
View File
@@ -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)
}
+488
View File
@@ -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();
`
+1 -1
View File
@@ -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 {
+1 -1
View File
@@ -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
View File
@@ -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
}