mirror of
https://github.com/yunionio/cloudpods.git
synced 2026-05-22 04:19:43 +08:00
1873 lines
53 KiB
Go
1873 lines
53 KiB
Go
// Copyright 2019 Yunion
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
package fsdriver
|
|
|
|
import (
|
|
"fmt"
|
|
"path"
|
|
"regexp"
|
|
"strconv"
|
|
"strings"
|
|
"syscall"
|
|
|
|
"github.com/pkg/errors"
|
|
"gopkg.in/yaml.v2"
|
|
|
|
"yunion.io/x/jsonutils"
|
|
"yunion.io/x/log"
|
|
"yunion.io/x/pkg/util/netutils"
|
|
"yunion.io/x/pkg/utils"
|
|
|
|
"yunion.io/x/onecloud/pkg/apis"
|
|
api "yunion.io/x/onecloud/pkg/apis/compute"
|
|
"yunion.io/x/onecloud/pkg/cloudcommon/types"
|
|
deployapi "yunion.io/x/onecloud/pkg/hostman/hostdeployer/apis"
|
|
"yunion.io/x/onecloud/pkg/util/coreosutils"
|
|
"yunion.io/x/onecloud/pkg/util/fileutils2"
|
|
"yunion.io/x/onecloud/pkg/util/fstabutils"
|
|
"yunion.io/x/onecloud/pkg/util/netutils2"
|
|
"yunion.io/x/onecloud/pkg/util/procutils"
|
|
"yunion.io/x/onecloud/pkg/util/seclib2"
|
|
"yunion.io/x/onecloud/pkg/util/sysutils"
|
|
)
|
|
|
|
const (
|
|
ROOT_USER = "root"
|
|
YUNIONROOT_USER = "cloudroot"
|
|
)
|
|
|
|
var (
|
|
NetDevPrefix = "eth"
|
|
)
|
|
|
|
type sLinuxRootFs struct {
|
|
*sGuestRootFsDriver
|
|
}
|
|
|
|
func newLinuxRootFs(part IDiskPartition) *sLinuxRootFs {
|
|
return &sLinuxRootFs{
|
|
sGuestRootFsDriver: newGuestRootFsDriver(part),
|
|
}
|
|
}
|
|
|
|
func (l *sLinuxRootFs) RootSignatures() []string {
|
|
return []string{"/bin", "/etc", "/boot", "/lib", "/usr"}
|
|
}
|
|
|
|
func getHostname(hostname, domain string) string {
|
|
if len(domain) > 0 {
|
|
return fmt.Sprintf("%s.%s", hostname, domain)
|
|
} else {
|
|
return hostname
|
|
}
|
|
}
|
|
|
|
func (l *sLinuxRootFs) DeployHosts(rootFs IDiskPartition, hostname, domain string, ips []string) error {
|
|
var etcHosts = "/etc/hosts"
|
|
var oldHostFile string
|
|
if rootFs.Exists(etcHosts, false) {
|
|
oldhf, err := rootFs.FileGetContents(etcHosts, false)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
oldHostFile = string(oldhf)
|
|
}
|
|
hf := make(fileutils2.HostsFile, 0)
|
|
hf.Parse(oldHostFile)
|
|
hf.Add("127.0.0.1", "localhost")
|
|
for _, ip := range ips {
|
|
hf.Add(ip, getHostname(hostname, domain), hostname)
|
|
}
|
|
return rootFs.FilePutContents(etcHosts, hf.String(), false, false)
|
|
}
|
|
|
|
func (l *sLinuxRootFs) GetLoginAccount(rootFs IDiskPartition, sUser string, defaultRootUser bool, windowsDefaultAdminUser bool) (string, error) {
|
|
if len(sUser) > 0 {
|
|
if _, err := rootFs.CheckOrAddUser(sUser, "", false); err != nil && !strings.Contains(err.Error(), "already exists") {
|
|
return "", fmt.Errorf("UserAdd %s: %v", sUser, err)
|
|
}
|
|
if err := l.EnableUserSudo(rootFs, sUser); err != nil {
|
|
return "", fmt.Errorf("EnableUserSudo: %s", err)
|
|
}
|
|
return sUser, nil
|
|
}
|
|
var selUsr string
|
|
if defaultRootUser && rootFs.Exists("/root", false) {
|
|
selUsr = ROOT_USER
|
|
} else {
|
|
usrs := rootFs.ListDir("/home", false)
|
|
for _, usr := range usrs {
|
|
if usr == YUNIONROOT_USER {
|
|
continue
|
|
}
|
|
if len(selUsr) == 0 || len(selUsr) > len(usr) {
|
|
selUsr = usr
|
|
}
|
|
}
|
|
if len(selUsr) == 0 && rootFs.Exists("/root", false) {
|
|
selUsr = ROOT_USER
|
|
}
|
|
}
|
|
return selUsr, nil
|
|
}
|
|
|
|
func (l *sLinuxRootFs) ChangeUserPasswd(rootFs IDiskPartition, account, gid, publicKey, password string) (string, error) {
|
|
var secret string
|
|
var err error
|
|
err = rootFs.Passwd(account, password, false)
|
|
if err == nil {
|
|
if len(publicKey) > 0 {
|
|
secret, err = seclib2.EncryptBase64(publicKey, password)
|
|
} else {
|
|
secret, err = utils.EncryptAESBase64(gid, password)
|
|
}
|
|
} else {
|
|
return "", fmt.Errorf("ChangeUserPasswd error: %v", err)
|
|
}
|
|
return secret, err
|
|
}
|
|
|
|
func (l *sLinuxRootFs) DeployPublicKey(rootFs IDiskPartition, selUsr string, pubkeys *deployapi.SSHKeys) error {
|
|
var usrDir string
|
|
if selUsr == "root" {
|
|
usrDir = "/root"
|
|
} else {
|
|
usrDir = path.Join("/home", selUsr)
|
|
}
|
|
return DeployAuthorizedKeys(rootFs, usrDir, pubkeys, false)
|
|
}
|
|
|
|
func (l *sLinuxRootFs) DeployYunionroot(rootFs IDiskPartition, pubkeys *deployapi.SSHKeys, isInit, enableCloudInit bool) error {
|
|
l.DisableSelinux(rootFs)
|
|
if !enableCloudInit && isInit {
|
|
l.DisableCloudinit(rootFs)
|
|
}
|
|
var yunionroot = YUNIONROOT_USER
|
|
rootdir := path.Join(cloudrootDirectory, yunionroot)
|
|
var err error
|
|
if rootdir, err = rootFs.CheckOrAddUser(yunionroot, cloudrootDirectory, true); err != nil {
|
|
return errors.Wrap(err, "unable to CheckOrAddUser")
|
|
}
|
|
err = DeployAuthorizedKeys(rootFs, rootdir, pubkeys, true)
|
|
if err != nil {
|
|
log.Infof("DeployAuthorizedKeys error: %s", err.Error())
|
|
return fmt.Errorf("DeployAuthorizedKeys: %v", err)
|
|
}
|
|
if err := l.EnableUserSudo(rootFs, yunionroot); err != nil {
|
|
return fmt.Errorf("EnableUserSudo: %v", err)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (l *sLinuxRootFs) EnableUserSudo(rootFs IDiskPartition, user string) error {
|
|
var sudoDir = "/etc/sudoers.d"
|
|
var content = fmt.Sprintf("%s ALL=(ALL) NOPASSWD:ALL\n", user)
|
|
if rootFs.Exists(sudoDir, false) {
|
|
filepath := path.Join(sudoDir, fmt.Sprintf("90-%s-users", user))
|
|
err := rootFs.FilePutContents(filepath, content, false, false)
|
|
if err != nil {
|
|
return fmt.Errorf("Write contents to %s: %v", filepath, err)
|
|
}
|
|
return rootFs.Chmod(filepath, syscall.S_IRUSR|syscall.S_IRGRP, false)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (l *sLinuxRootFs) DisableSelinux(rootFs IDiskPartition) {
|
|
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 rootFs.Exists(selinuxConfig, false) {
|
|
if err := rootFs.FilePutContents(selinuxConfig, content, false, false); err != nil {
|
|
log.Errorf("DisableSelinux error: %v", err)
|
|
}
|
|
}
|
|
}
|
|
|
|
func (l *sLinuxRootFs) DisableCloudinit(rootFs IDiskPartition) {
|
|
cloudDir := "/etc/cloud"
|
|
cloudDisableFile := "/etc/cloud/cloud-init.disabled"
|
|
if rootFs.Exists(cloudDir, false) {
|
|
if err := rootFs.FilePutContents(cloudDisableFile, "", false, false); err != nil {
|
|
log.Errorf("DisableCloudinit error: %v", err)
|
|
}
|
|
}
|
|
}
|
|
|
|
func (l *sLinuxRootFs) DeployFstabScripts(rootFs IDiskPartition, disks []*deployapi.Disk) error {
|
|
fstabcont, err := 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))
|
|
if fstab != nil {
|
|
fstab = fstab.RemoveDevices(len(disks))
|
|
} else {
|
|
_fstab := make(fstabutils.FsTab, 0)
|
|
fstab = &_fstab
|
|
}
|
|
|
|
for i := 1; i < len(disks); i++ {
|
|
diskId := disks[i].DiskId
|
|
if len(diskId) == 0 {
|
|
diskId = "None"
|
|
}
|
|
dev := fmt.Sprintf("UUID=%s", diskId)
|
|
if !fstab.IsExists(dev) {
|
|
fs := disks[i].Fs
|
|
if len(fs) > 0 {
|
|
if fs == "swap" {
|
|
rec = fmt.Sprintf("%s none %s sw 0 0", dev, fs)
|
|
} else {
|
|
mtPath := disks[i].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 rootFs.FilePutContents("/etc/fstab", cf, false, false)
|
|
}
|
|
|
|
func (l *sLinuxRootFs) DeployNetworkingScripts(rootFs IDiskPartition, nics []*types.SServerNic) error {
|
|
udevPath := "/etc/udev/rules.d/"
|
|
if rootFs.Exists(udevPath, false) {
|
|
rules := rootFs.ListDir(udevPath, false)
|
|
for _, rule := range rules {
|
|
if strings.Index(rule, "persistent-net.rules") > 0 {
|
|
rootFs.Remove(path.Join(udevPath, rule), false)
|
|
} else if strings.Index(rule, "persistent-cd.rules") > 0 {
|
|
if err := rootFs.FilePutContents(path.Join(udevPath, rule), "", false, false); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
}
|
|
var nicRules string
|
|
for _, nic := range nics {
|
|
nicRules += `KERNEL=="*", SUBSYSTEM=="net", ACTION=="add", `
|
|
nicRules += `DRIVERS=="?*", `
|
|
mac := nic.Mac
|
|
nicRules += fmt.Sprintf(`ATTR{address}=="%s", ATTR{type}=="1", `, strings.ToLower(mac))
|
|
idx := nic.Index
|
|
nicRules += fmt.Sprintf("NAME=\"%s%d\"\n", NetDevPrefix, idx)
|
|
}
|
|
if err := 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 := rootFs.FilePutContents(path.Join(udevPath,
|
|
"90-usb-tablet-remote-wakeup.rules"), usbRules, false, false); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
// deploy docker mtu
|
|
{
|
|
minMtu := -1
|
|
for _, nic := range nics {
|
|
if nic.Mtu > 0 && (minMtu > nic.Mtu || minMtu < 0) {
|
|
minMtu = nic.Mtu
|
|
}
|
|
}
|
|
const dockerDaemonConfPath = "/etc/docker/daemon.json"
|
|
var daemonConfJson jsonutils.JSONObject
|
|
if rootFs.Exists(dockerDaemonConfPath, false) {
|
|
content, _ := rootFs.FileGetContents(dockerDaemonConfPath, false)
|
|
if len(content) > 0 {
|
|
daemonConfJson, _ = jsonutils.Parse(content)
|
|
}
|
|
} else {
|
|
const modeRwxOwner = syscall.S_IRUSR | syscall.S_IWUSR | syscall.S_IXUSR
|
|
if err := rootFs.Mkdir("/etc/docker", modeRwxOwner, false); err != nil {
|
|
return errors.Wrap(err, "mkdir /etc/docker fail")
|
|
}
|
|
}
|
|
if daemonConfJson == nil {
|
|
daemonConfJson = jsonutils.NewDict()
|
|
}
|
|
daemonConfJson.(*jsonutils.JSONDict).Set("mtu", jsonutils.NewInt(int64(minMtu)))
|
|
if err := rootFs.FilePutContents(dockerDaemonConfPath, daemonConfJson.PrettyString(), false, false); err != nil {
|
|
return errors.Wrapf(err, "write %s fail", dockerDaemonConfPath)
|
|
}
|
|
}
|
|
// deploy ssh host key
|
|
{
|
|
err := rootFs.GenerateSshHostKeys()
|
|
if err != nil {
|
|
// ignore error
|
|
log.Errorf("rootFs.GenerateSshHostKeys fail %s", err)
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (l *sLinuxRootFs) DeployStandbyNetworkingScripts(rootFs IDiskPartition, nics, nicsStandby []*types.SServerNic) error {
|
|
var udevPath = "/etc/udev/rules.d/"
|
|
var nicRules string
|
|
for _, nic := range nicsStandby {
|
|
if len(nic.NicType) == 0 || nic.NicType != api.NIC_TYPE_IPMI {
|
|
nicRules += `KERNEL=="*", SUBSYSTEM=="net", ACTION=="add", `
|
|
nicRules += `DRIVERS=="?*", `
|
|
mac := nic.Mac
|
|
nicRules += fmt.Sprintf(`ATTR{address}=="%s", ATTR{type}=="1", `, strings.ToLower(mac))
|
|
idx := nic.Index
|
|
nicRules += fmt.Sprintf(`NAME="%s%d"\n`, NetDevPrefix, idx)
|
|
}
|
|
}
|
|
if err := rootFs.FilePutContents(path.Join(udevPath, "70-persistent-net.rules"), nicRules, true, false); err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (l *sLinuxRootFs) GetOs() string {
|
|
return "Linux"
|
|
}
|
|
|
|
func (l *sLinuxRootFs) GetArch(rootFs IDiskPartition) string {
|
|
if rootFs.Exists("/lib64", false) && rootFs.Exists("/usr/lib64", false) {
|
|
files := rootFs.ListDir("/lib64", false)
|
|
for i := 0; i < len(files); i++ {
|
|
if strings.HasPrefix(files[i], "ld-") {
|
|
if strings.Contains(files[i], apis.OS_ARCH_AARCH64) {
|
|
return apis.OS_ARCH_AARCH64
|
|
} else if strings.Contains(files[i], "x86") {
|
|
return apis.OS_ARCH_X86_64
|
|
}
|
|
}
|
|
}
|
|
return apis.OS_ARCH_X86_64
|
|
} else {
|
|
files := rootFs.ListDir("/lib", false)
|
|
for i := 0; i < len(files); i++ {
|
|
if strings.HasPrefix(files[i], "ld-") {
|
|
if strings.Contains(files[i], "arm") {
|
|
return apis.OS_ARCH_AARCH32
|
|
}
|
|
}
|
|
}
|
|
return apis.OS_ARCH_X86_32
|
|
}
|
|
}
|
|
|
|
func (l *sLinuxRootFs) PrepareFsForTemplate(rootFs IDiskPartition) error {
|
|
// clean /etc/fstab
|
|
if rootFs.Exists("/etc/fstab", false) {
|
|
fstabcont, _ := rootFs.FileGetContents("/etc/fstab", false)
|
|
fstab := fstabutils.FSTabFile(string(fstabcont))
|
|
var cf string
|
|
if fstab != nil {
|
|
fstab = fstab.RemoveDevices(1)
|
|
cf = fstab.ToConf()
|
|
}
|
|
if err := rootFs.FilePutContents("/etc/fstab", cf, false, false); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
// rm /etc/ssh/*_key.*
|
|
if rootFs.Exists("/etc/ssh", false) {
|
|
for _, f := range l.rootFs.ListDir("/etc/ssh", false) {
|
|
if strings.HasSuffix(f, "_key") || strings.HasSuffix(f, "_key.pub") {
|
|
rootFs.Remove("/etc/ssh/"+f, false)
|
|
}
|
|
}
|
|
}
|
|
// clean cloud-init
|
|
if rootFs.Exists("/var/lib/cloud", false) {
|
|
if err := rootFs.Cleandir("/var/lib/cloud", false, false); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
cloudDisableFile := "/etc/cloud/cloud-init.disabled"
|
|
if rootFs.Exists(cloudDisableFile, false) {
|
|
rootFs.Remove(cloudDisableFile, false)
|
|
}
|
|
// clean /tmp /var/log /var/cache /var/spool /var/run
|
|
for _, dir := range []string{"/tmp", "/var/tmp"} {
|
|
if rootFs.Exists(dir, false) {
|
|
if err := rootFs.Cleandir(dir, false, false); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
}
|
|
for _, dir := range []string{
|
|
"/var/log",
|
|
"/var/cache",
|
|
"/usr/local/var/log",
|
|
"/usr/local/var/cache",
|
|
} {
|
|
if rootFs.Exists(dir, false) {
|
|
if err := l.rootFs.Zerofiles(dir, false); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
}
|
|
for _, dir := range []string{
|
|
"/var/spool",
|
|
"/var/run",
|
|
"/run",
|
|
"/usr/local/var/spool",
|
|
"/usr/local/var/run",
|
|
"/etc/openvswitch",
|
|
} {
|
|
if rootFs.Exists(dir, false) {
|
|
if err := rootFs.Cleandir(dir, true, true); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (l *sLinuxRootFs) getSerialPorts(rootFs IDiskPartition) []string {
|
|
if !rootFs.SupportSerialPorts() {
|
|
return nil
|
|
}
|
|
// XXX HACK, only sshpart.SSHPartition support this
|
|
var confpath = "/proc/tty/driver/serial"
|
|
content, err := rootFs.FileGetContentsByPath(confpath)
|
|
if err != nil {
|
|
log.Errorf("Get %s error: %v", confpath, err)
|
|
return nil
|
|
}
|
|
ttys := sysutils.GetSerialPorts(strings.Split(string(content), "\n"))
|
|
log.Infof("Get serial ports content:\n%s, find serial ttys: %#v", string(content), ttys)
|
|
return ttys
|
|
}
|
|
|
|
func (l *sLinuxRootFs) enableSerialConsoleInitCentos(rootFs IDiskPartition) error {
|
|
// http://www.jonno.org/drupal/node/10
|
|
var err error
|
|
for _, tty := range l.getSerialPorts(rootFs) {
|
|
content := fmt.Sprintf(
|
|
`stop on runlevel [016]
|
|
start on runlevel [345]
|
|
instance %s
|
|
respawn
|
|
pre-start exec /sbin/securetty %s
|
|
exec /sbin/agetty /dev/%s 115200 vt100`, tty, tty, tty)
|
|
err = rootFs.FilePutContents(fmt.Sprintf("/etc/init/%s.conf", tty), content, false, false)
|
|
}
|
|
return err
|
|
}
|
|
|
|
func (l *sLinuxRootFs) enableSerialConsoleRootLogin(rootFs IDiskPartition, tty string) error {
|
|
secureTTYFile := "/etc/securetty"
|
|
content, err := rootFs.FileGetContents(secureTTYFile, false)
|
|
if err != nil {
|
|
return errors.Wrapf(err, "get contents of %s", secureTTYFile)
|
|
}
|
|
secureTTYs := sysutils.GetSecureTTYs(strings.Split(string(content), "\n"))
|
|
if utils.IsInStringArray(tty, secureTTYs) {
|
|
return nil
|
|
}
|
|
return rootFs.FilePutContents(secureTTYFile, fmt.Sprintf("\n%s", tty), true, false)
|
|
}
|
|
|
|
func (l *sLinuxRootFs) enableSerialConsoleInit(rootFs IDiskPartition) error {
|
|
// https://help.ubuntu.com/community/SerialConsoleHowto
|
|
var err error
|
|
for _, tty := range l.getSerialPorts(rootFs) {
|
|
if err := l.enableSerialConsoleRootLogin(rootFs, tty); err != nil {
|
|
log.Errorf("Enable %s root login: %v", tty, err)
|
|
}
|
|
content := fmt.Sprintf(
|
|
`start on stopped rc or RUNLEVEL=[12345]
|
|
stop on runlevel [!12345]
|
|
respawn
|
|
exec /sbin/getty -L 115200 %s vt102`, tty)
|
|
err = rootFs.FilePutContents(fmt.Sprintf("/etc/init/%s.conf", tty), content, false, false)
|
|
}
|
|
return err
|
|
}
|
|
|
|
func (l *sLinuxRootFs) disableSerialConsoleInit(rootFs IDiskPartition) {
|
|
for _, tty := range l.getSerialPorts(rootFs) {
|
|
path := fmt.Sprintf("/etc/init/%s.conf", tty)
|
|
if rootFs.Exists(path, false) {
|
|
rootFs.Remove(path, false)
|
|
}
|
|
}
|
|
}
|
|
|
|
func (l *sLinuxRootFs) enableSerialConsoleSystemd(rootFs IDiskPartition) error {
|
|
for _, tty := range l.getSerialPorts(rootFs) {
|
|
if err := l.enableSerialConsoleRootLogin(rootFs, tty); err != nil {
|
|
log.Errorf("Enable %s root login: %v", tty, err)
|
|
}
|
|
sPath := fmt.Sprintf("/etc/systemd/system/getty.target.wants/getty@%s.service", tty)
|
|
if rootFs.Exists(sPath, false) {
|
|
rootFs.Remove(sPath, false)
|
|
}
|
|
if err := rootFs.Symlink("/usr/lib/systemd/system/getty@.service", sPath, false); err != nil {
|
|
return errors.Wrapf(err, "Symbol link tty %s", tty)
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (l *sLinuxRootFs) disableSerialConsoleSystemd(rootFs IDiskPartition) {
|
|
for _, tty := range l.getSerialPorts(rootFs) {
|
|
sPath := fmt.Sprintf("/etc/systemd/system/getty.target.wants/getty@%s.service", tty)
|
|
if rootFs.Exists(sPath, false) {
|
|
rootFs.Remove(sPath, false)
|
|
}
|
|
}
|
|
}
|
|
|
|
func (l *sLinuxRootFs) dirWalk(part IDiskPartition, sPath string, wF func(path string, isDir bool) bool) bool {
|
|
stat := part.Stat(sPath, false)
|
|
if !stat.IsDir() {
|
|
if wF(sPath, false) {
|
|
return true
|
|
}
|
|
return false
|
|
}
|
|
if wF(sPath, true) {
|
|
return true
|
|
}
|
|
for _, subPath := range part.ListDir(sPath, false) {
|
|
if l.dirWalk(part, path.Join(sPath, subPath), wF) {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
func (l *sLinuxRootFs) DetectIsUEFISupport(part IDiskPartition) bool {
|
|
// ref: https://wiki.archlinux.org/title/EFI_system_partition#Check_for_an_existing_partition
|
|
// To confirm this is the ESP, mount it and check whether it contains a directory named EFI,
|
|
// if it does this is definitely the ESP.
|
|
efiDir := "/EFI"
|
|
exits := part.Exists(efiDir, false)
|
|
if !exits {
|
|
return false
|
|
}
|
|
|
|
hasEFIFirmware := false
|
|
|
|
l.dirWalk(part, efiDir, func(path string, isDir bool) bool {
|
|
if isDir {
|
|
return false
|
|
}
|
|
// check file is UEFI firmware
|
|
if strings.HasSuffix(path, ".efi") {
|
|
log.Infof("EFI firmware %s found", path)
|
|
hasEFIFirmware = true
|
|
return true
|
|
}
|
|
// continue walk
|
|
return false
|
|
})
|
|
|
|
return hasEFIFirmware
|
|
}
|
|
|
|
func (l *sLinuxRootFs) IsCloudinitInstall() bool {
|
|
return l.GetPartition().Exists("/usr/bin/cloud-init", false)
|
|
}
|
|
|
|
type sDebianLikeRootFs struct {
|
|
*sLinuxRootFs
|
|
}
|
|
|
|
func newDebianLikeRootFs(part IDiskPartition) *sDebianLikeRootFs {
|
|
return &sDebianLikeRootFs{
|
|
sLinuxRootFs: newLinuxRootFs(part),
|
|
}
|
|
}
|
|
|
|
func (d *sDebianLikeRootFs) PrepareFsForTemplate(rootFs IDiskPartition) error {
|
|
if err := d.sLinuxRootFs.PrepareFsForTemplate(rootFs); err != nil {
|
|
return err
|
|
}
|
|
// clean /etc/network/interface
|
|
if rootFs.Exists("/etc/network/interface", false) {
|
|
fn := "/etc/network/interfaces"
|
|
var cmds strings.Builder
|
|
cmds.WriteString("auto lo\n")
|
|
cmds.WriteString("iface lo inet loopback\n\n")
|
|
if err := rootFs.FilePutContents(fn, cmds.String(), false, false); err != nil {
|
|
return errors.Wrap(err, "file put content /etc/network/interface")
|
|
}
|
|
}
|
|
|
|
// clean /etc/netplan/*
|
|
netplanDir := "/etc/netplan/"
|
|
if rootFs.Exists(netplanDir, false) {
|
|
for _, f := range rootFs.ListDir(netplanDir, false) {
|
|
rootFs.Remove(netplanDir+f, false)
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (d *sDebianLikeRootFs) GetReleaseInfo(rootFs IDiskPartition, driver IDebianRootFsDriver) *deployapi.ReleaseInfo {
|
|
version, err := rootFs.FileGetContents(driver.VersionFilePath(), false)
|
|
if err != nil {
|
|
log.Errorf("Get %s error: %v", driver.VersionFilePath(), err)
|
|
return nil
|
|
}
|
|
versionStr := strings.TrimSpace(string(version))
|
|
return &deployapi.ReleaseInfo{
|
|
Distro: driver.DistroName(),
|
|
Version: versionStr,
|
|
Arch: d.GetArch(rootFs),
|
|
}
|
|
}
|
|
|
|
func (d *sDebianLikeRootFs) RootSignatures() []string {
|
|
sig := d.sLinuxRootFs.RootSignatures()
|
|
return append([]string{"/etc/hostname"}, sig...)
|
|
}
|
|
|
|
func (d *sDebianLikeRootFs) DeployHostname(rootFs IDiskPartition, hn, domain string) error {
|
|
return rootFs.FilePutContents("/etc/hostname", hn, false, false)
|
|
}
|
|
|
|
func getNicTeamingConfigCmds(slaves []*types.SServerNic) string {
|
|
var cmds strings.Builder
|
|
cmds.WriteString(" bond-mode 4\n")
|
|
cmds.WriteString(" bond-miimon 100\n")
|
|
cmds.WriteString(" bond-lacp-rate 1\n")
|
|
cmds.WriteString(" bond-xmit_hash_policy 1\n")
|
|
cmds.WriteString(" bond-slaves")
|
|
for i := range slaves {
|
|
cmds.WriteString(" ")
|
|
cmds.WriteString(slaves[i].Name)
|
|
}
|
|
cmds.WriteString("\n")
|
|
return cmds.String()
|
|
}
|
|
|
|
func (d *sDebianLikeRootFs) deployNetplanConfigFile(rootFs IDiskPartition, nics []*types.SServerNic) error {
|
|
netplanDir := "/etc/netplan/"
|
|
dirExists := rootFs.Exists(netplanDir, false)
|
|
if !dirExists {
|
|
return nil
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (d *sDebianLikeRootFs) DeployNetworkingScripts(rootFs IDiskPartition, nics []*types.SServerNic) error {
|
|
if err := d.sLinuxRootFs.DeployNetworkingScripts(rootFs, nics); err != nil {
|
|
return err
|
|
}
|
|
fn := "/etc/network/interfaces"
|
|
var cmds strings.Builder
|
|
cmds.WriteString("auto lo\n")
|
|
cmds.WriteString("iface lo inet loopback\n\n")
|
|
|
|
// ToServerNics(nics)
|
|
allNics, bondNics := convertNicConfigs(nics)
|
|
|
|
netplanDir := "/etc/netplan"
|
|
if rootFs.Exists(netplanDir, false) {
|
|
for _, f := range rootFs.ListDir(netplanDir, false) {
|
|
rootFs.Remove(netplanDir+f, false)
|
|
}
|
|
netplanConfig := NewNetplanConfig(allNics, bondNics)
|
|
if err := rootFs.FilePutContents(path.Join(netplanDir, "config.yaml"), netplanConfig.YAMLString(), false, false); err != nil {
|
|
return errors.Wrap(err, "Put netplan config")
|
|
}
|
|
}
|
|
|
|
mainNic, err := getMainNic(allNics)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
var mainIp string
|
|
if mainNic != nil {
|
|
mainIp = mainNic.Ip
|
|
}
|
|
|
|
var systemdResolveConfig strings.Builder
|
|
dnss := []string{}
|
|
domains := []string{}
|
|
for i := range allNics {
|
|
nicDesc := allNics[i]
|
|
cmds.WriteString(fmt.Sprintf("auto %s\n", nicDesc.Name))
|
|
if nicDesc.TeamingMaster != nil {
|
|
cmds.WriteString(fmt.Sprintf("iface %s inet manual\n", nicDesc.Name))
|
|
cmds.WriteString(fmt.Sprintf(" bond-master %s\n", nicDesc.TeamingMaster.Name))
|
|
cmds.WriteString("\n")
|
|
} else if nicDesc.Virtual {
|
|
cmds.WriteString(fmt.Sprintf("iface %s inet static\n", nicDesc.Name))
|
|
cmds.WriteString(fmt.Sprintf(" address %s\n", netutils2.PSEUDO_VIP))
|
|
cmds.WriteString(" netmask 255.255.255.255\n")
|
|
cmds.WriteString("\n")
|
|
} else if nicDesc.Manual {
|
|
netmask := netutils2.Netlen2Mask(int(nicDesc.Masklen))
|
|
cmds.WriteString(fmt.Sprintf("iface %s inet static\n", nicDesc.Name))
|
|
cmds.WriteString(fmt.Sprintf(" address %s\n", nicDesc.Ip))
|
|
cmds.WriteString(fmt.Sprintf(" netmask %s\n", netmask))
|
|
if len(nicDesc.Gateway) > 0 && nicDesc.Ip == mainIp {
|
|
cmds.WriteString(fmt.Sprintf(" gateway %s\n", nicDesc.Gateway))
|
|
}
|
|
if nicDesc.Mtu > 0 {
|
|
cmds.WriteString(fmt.Sprintf(" mtu %d\n", nicDesc.Mtu))
|
|
}
|
|
var routes = make([][]string, 0)
|
|
netutils2.AddNicRoutes(&routes, nicDesc, mainIp, len(nics), privatePrefixes)
|
|
for _, r := range routes {
|
|
cmds.WriteString(fmt.Sprintf(" up route add -net %s gw %s || true\n", r[0], r[1]))
|
|
cmds.WriteString(fmt.Sprintf(" down route del -net %s gw %s || true\n", r[0], r[1]))
|
|
}
|
|
dnslist := netutils2.GetNicDns(nicDesc)
|
|
if len(dnslist) > 0 {
|
|
cmds.WriteString(fmt.Sprintf(" dns-nameservers %s\n", strings.Join(dnslist, " ")))
|
|
dnss = append(dnss, dnslist...)
|
|
if len(nicDesc.Domain) > 0 {
|
|
cmds.WriteString(fmt.Sprintf(" dns-search %s\n", nicDesc.Domain))
|
|
domains = append(domains, nicDesc.Domain)
|
|
}
|
|
}
|
|
if len(nicDesc.TeamingSlaves) > 0 {
|
|
cmds.WriteString(getNicTeamingConfigCmds(nicDesc.TeamingSlaves))
|
|
}
|
|
cmds.WriteString("\n")
|
|
} else {
|
|
cmds.WriteString(fmt.Sprintf("iface %s inet dhcp\n", nicDesc.Name))
|
|
if len(nicDesc.TeamingSlaves) > 0 {
|
|
cmds.WriteString(getNicTeamingConfigCmds(nicDesc.TeamingSlaves))
|
|
}
|
|
cmds.WriteString("\n")
|
|
}
|
|
}
|
|
|
|
if len(dnss) != 0 {
|
|
systemdResolveConfig.WriteString("[Resolve]\n")
|
|
systemdResolveConfig.WriteString(fmt.Sprintf("DNS=%s\n", strings.Join(dnss, " ")))
|
|
if len(domains) != 0 {
|
|
systemdResolveConfig.WriteString(fmt.Sprintf("Domains=%s\n", strings.Join(domains, " ")))
|
|
}
|
|
systemdResolveFn := "/etc/systemd/resolved.conf"
|
|
content := systemdResolveConfig.String()
|
|
if err := rootFs.FilePutContents(systemdResolveFn, content, false, false); err != nil {
|
|
log.Warningf("Put %s to %s error: %v", content, systemdResolveFn, err)
|
|
}
|
|
}
|
|
log.Debugf("%s", cmds.String())
|
|
return rootFs.FilePutContents(fn, cmds.String(), false, false)
|
|
}
|
|
|
|
type SDebianRootFs struct {
|
|
*sDebianLikeRootFs
|
|
}
|
|
|
|
func NewDebianRootFs(part IDiskPartition) IRootFsDriver {
|
|
driver := new(SDebianRootFs)
|
|
driver.sDebianLikeRootFs = newDebianLikeRootFs(part)
|
|
return driver
|
|
}
|
|
|
|
func (d *SDebianRootFs) String() string {
|
|
return "DebianRootFs"
|
|
}
|
|
|
|
func (d *SDebianRootFs) GetName() string {
|
|
return "Debian"
|
|
}
|
|
|
|
func (d *SDebianRootFs) DistroName() string {
|
|
return d.GetName()
|
|
}
|
|
|
|
func (d *SDebianRootFs) VersionFilePath() string {
|
|
return "/etc/debian_version"
|
|
}
|
|
|
|
func (d *SDebianRootFs) rootSignatures(driver IDebianRootFsDriver) []string {
|
|
sig := d.sDebianLikeRootFs.RootSignatures()
|
|
return append([]string{driver.VersionFilePath()}, sig...)
|
|
}
|
|
|
|
func (d *SDebianRootFs) RootSignatures() []string {
|
|
return d.rootSignatures(d)
|
|
}
|
|
|
|
func (d *SDebianRootFs) RootExcludeSignatures() []string {
|
|
return []string{"/etc/lsb-release"}
|
|
}
|
|
|
|
func (d *SDebianRootFs) GetReleaseInfo(rootFs IDiskPartition) *deployapi.ReleaseInfo {
|
|
return d.sDebianLikeRootFs.GetReleaseInfo(rootFs, d)
|
|
}
|
|
|
|
type SCirrosRootFs struct {
|
|
*SDebianRootFs
|
|
}
|
|
|
|
func NewCirrosRootFs(part IDiskPartition) IRootFsDriver {
|
|
driver := new(SCirrosRootFs)
|
|
driver.SDebianRootFs = NewDebianRootFs(part).(*SDebianRootFs)
|
|
return driver
|
|
}
|
|
|
|
func (d *SCirrosRootFs) GetName() string {
|
|
return "Cirros"
|
|
}
|
|
|
|
func (d *SCirrosRootFs) String() string {
|
|
return "CirrosRootFs"
|
|
}
|
|
|
|
func (d *SCirrosRootFs) DistroName() string {
|
|
return d.GetName()
|
|
}
|
|
|
|
func (d *SCirrosRootFs) VersionFilePath() string {
|
|
return "/etc/br-version"
|
|
}
|
|
|
|
func (d *SCirrosRootFs) GetReleaseInfo(rootFs IDiskPartition) *deployapi.ReleaseInfo {
|
|
return d.SDebianRootFs.sDebianLikeRootFs.GetReleaseInfo(rootFs, d)
|
|
}
|
|
|
|
func (d *SCirrosRootFs) RootSignatures() []string {
|
|
return d.rootSignatures(d)
|
|
}
|
|
|
|
type SCirrosNewRootFs struct {
|
|
*SDebianRootFs
|
|
}
|
|
|
|
func NewCirrosNewRootFs(part IDiskPartition) IRootFsDriver {
|
|
driver := new(SCirrosNewRootFs)
|
|
driver.SDebianRootFs = NewDebianRootFs(part).(*SDebianRootFs)
|
|
return driver
|
|
}
|
|
|
|
func (d *SCirrosNewRootFs) GetName() string {
|
|
return "Cirros"
|
|
}
|
|
|
|
func (d *SCirrosNewRootFs) String() string {
|
|
return "CirrosNewRootFs"
|
|
}
|
|
|
|
func (d *SCirrosNewRootFs) DistroName() string {
|
|
return d.GetName()
|
|
}
|
|
|
|
func (d *SCirrosNewRootFs) VersionFilePath() string {
|
|
return "/etc/cirros/version"
|
|
}
|
|
|
|
func (d *SCirrosNewRootFs) RootSignatures() []string {
|
|
return d.rootSignatures(d)
|
|
}
|
|
|
|
func (d *SCirrosNewRootFs) GetReleaseInfo(rootFs IDiskPartition) *deployapi.ReleaseInfo {
|
|
return d.SDebianRootFs.sDebianLikeRootFs.GetReleaseInfo(rootFs, d)
|
|
}
|
|
|
|
type SUbuntuRootFs struct {
|
|
*sDebianLikeRootFs
|
|
}
|
|
|
|
func NewUbuntuRootFs(part IDiskPartition) IRootFsDriver {
|
|
driver := new(SUbuntuRootFs)
|
|
driver.sDebianLikeRootFs = newDebianLikeRootFs(part)
|
|
return driver
|
|
}
|
|
|
|
func (d *SUbuntuRootFs) RootSignatures() []string {
|
|
sig := d.sDebianLikeRootFs.RootSignatures()
|
|
return append([]string{"/etc/lsb-release"}, sig...)
|
|
}
|
|
|
|
func (d *SUbuntuRootFs) GetName() string {
|
|
return "Ubuntu"
|
|
}
|
|
|
|
func (d *SUbuntuRootFs) String() string {
|
|
return "UbuntuRootFs"
|
|
}
|
|
|
|
func (d *SUbuntuRootFs) GetReleaseInfo(rootFs IDiskPartition) *deployapi.ReleaseInfo {
|
|
distroKey := "DISTRIB_RELEASE="
|
|
rel, err := rootFs.FileGetContents("/etc/lsb-release", false)
|
|
if err != nil {
|
|
log.Errorf("Get ubuntu release info error: %v", err)
|
|
return nil
|
|
}
|
|
var version string
|
|
lines := strings.Split(string(rel), "\n")
|
|
for _, l := range lines {
|
|
if strings.HasPrefix(l, distroKey) {
|
|
version = strings.TrimSpace(l[len(distroKey):])
|
|
}
|
|
}
|
|
return deployapi.NewReleaseInfo(d.GetName(), version, d.GetArch(rootFs))
|
|
}
|
|
|
|
func (d *SUbuntuRootFs) EnableSerialConsole(rootFs IDiskPartition, sysInfo *jsonutils.JSONDict) error {
|
|
relInfo := d.GetReleaseInfo(rootFs)
|
|
ver := strings.Split(relInfo.Version, ".")
|
|
verInt, _ := strconv.Atoi(ver[0])
|
|
if verInt < 16 {
|
|
return d.enableSerialConsoleInit(rootFs)
|
|
}
|
|
return d.enableSerialConsoleSystemd(rootFs)
|
|
}
|
|
|
|
func (d *SUbuntuRootFs) DisableSerialConcole(rootFs IDiskPartition) error {
|
|
relInfo := d.GetReleaseInfo(rootFs)
|
|
ver := strings.Split(relInfo.Version, ".")
|
|
verInt, _ := strconv.Atoi(ver[0])
|
|
if verInt < 16 {
|
|
d.disableSerialConsoleInit(rootFs)
|
|
return nil
|
|
}
|
|
d.disableSerialConsoleSystemd(rootFs)
|
|
return nil
|
|
}
|
|
|
|
type SUKylinRootfs struct {
|
|
*SUbuntuRootFs
|
|
}
|
|
|
|
func NewUKylinRootfs(part IDiskPartition) *SUKylinRootfs {
|
|
return &SUKylinRootfs{SUbuntuRootFs: NewUbuntuRootFs(part).(*SUbuntuRootFs)}
|
|
}
|
|
|
|
func (d *SUKylinRootfs) GetName() string {
|
|
return "UbuntuKylin"
|
|
}
|
|
|
|
func (d *SUKylinRootfs) String() string {
|
|
return "UKylinuRootFs"
|
|
}
|
|
|
|
func (d *SUKylinRootfs) RootSignatures() []string {
|
|
sig := d.sDebianLikeRootFs.RootSignatures()
|
|
return append([]string{"/etc/lsb-release", "/etc/kylin-build"}, sig...)
|
|
}
|
|
|
|
type sRedhatLikeRootFs struct {
|
|
*sLinuxRootFs
|
|
}
|
|
|
|
func newRedhatLikeRootFs(part IDiskPartition) *sRedhatLikeRootFs {
|
|
return &sRedhatLikeRootFs{
|
|
sLinuxRootFs: newLinuxRootFs(part),
|
|
}
|
|
}
|
|
|
|
func (r *sRedhatLikeRootFs) PrepareFsForTemplate(rootFs IDiskPartition) error {
|
|
if err := r.sLinuxRootFs.PrepareFsForTemplate(rootFs); err != nil {
|
|
return err
|
|
}
|
|
return r.CleanNetworkScripts(rootFs)
|
|
}
|
|
|
|
func (r *sRedhatLikeRootFs) CleanNetworkScripts(rootFs IDiskPartition) error {
|
|
networkPath := "/etc/sysconfig/network-scripts"
|
|
files := rootFs.ListDir(networkPath, false)
|
|
for i := 0; i < len(files); i++ {
|
|
if strings.HasPrefix(files[i], "ifcfg-") && files[i] != "ifcfg-lo" {
|
|
rootFs.Remove(path.Join(networkPath, files[i]), false)
|
|
continue
|
|
}
|
|
if strings.HasPrefix(files[i], "route-") {
|
|
rootFs.Remove(path.Join(networkPath, files[i]), false)
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (r *sRedhatLikeRootFs) RootSignatures() []string {
|
|
sig := r.sLinuxRootFs.RootSignatures()
|
|
return append([]string{"/etc/sysconfig/network", "/etc/redhat-release"}, sig...)
|
|
}
|
|
|
|
func (r *sRedhatLikeRootFs) DeployHostname(rootFs IDiskPartition, hn, domain string) error {
|
|
var sPath = "/etc/sysconfig/network"
|
|
centosHn := ""
|
|
centosHn += "NETWORKING=yes\n"
|
|
centosHn += fmt.Sprintf("HOSTNAME=%s\n", getHostname(hn, domain))
|
|
if err := rootFs.FilePutContents(sPath, centosHn, false, false); err != nil {
|
|
return errors.Wrapf(err, "DeployHostname %s", sPath)
|
|
}
|
|
if err := rootFs.FilePutContents("/etc/hostname", hn, false, false); err != nil {
|
|
return errors.Wrapf(err, "DeployHostname %s", "/etc/hostname")
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (r *sRedhatLikeRootFs) Centos5DeployNetworkingScripts(rootFs IDiskPartition, nics []*types.SServerNic) error {
|
|
var udevPath = "/etc/udev/rules.d/"
|
|
if rootFs.Exists(udevPath, false) {
|
|
var nicRules = ""
|
|
for _, nic := range nics {
|
|
nicRules += `KERNEL=="*", `
|
|
nicRules += fmt.Sprintf(`SYSFS{address}=="%s", `, strings.ToLower(nic.Mac))
|
|
nicRules += fmt.Sprintf("NAME=\"%s%d\"\n", NetDevPrefix, nic.Index)
|
|
}
|
|
return rootFs.FilePutContents(path.Join(udevPath, "60-net.rules"),
|
|
nicRules, false, false)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func getMainNic(nics []*types.SServerNic) (*types.SServerNic, error) {
|
|
var mainIp netutils.IPV4Addr
|
|
var mainNic *types.SServerNic
|
|
for i := range nics {
|
|
if len(nics[i].Gateway) > 0 {
|
|
ipInt, err := netutils.NewIPV4Addr(nics[i].Ip)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if mainIp == 0 {
|
|
mainIp = ipInt
|
|
mainNic = nics[i]
|
|
} else if !netutils.IsPrivate(ipInt) && netutils.IsPrivate(mainIp) {
|
|
mainIp = ipInt
|
|
mainNic = nics[i]
|
|
}
|
|
}
|
|
}
|
|
return mainNic, nil
|
|
}
|
|
|
|
func (r *sRedhatLikeRootFs) enableBondingModule(rootFs IDiskPartition, bondNics []*types.SServerNic) error {
|
|
var content strings.Builder
|
|
for i := range bondNics {
|
|
content.WriteString("alias ")
|
|
content.WriteString(bondNics[i].Name)
|
|
content.WriteString(" bonding\n options ")
|
|
content.WriteString(bondNics[i].Name)
|
|
content.WriteString(" miimon=100 mode=4 lacp_rate=1 xmit_hash_policy=1\n")
|
|
}
|
|
return rootFs.FilePutContents("/etc/modprobe.d/bonding.conf", content.String(), false, false)
|
|
}
|
|
|
|
func (r *sRedhatLikeRootFs) deployNetworkingScripts(rootFs IDiskPartition, nics []*types.SServerNic, relInfo *deployapi.ReleaseInfo, forceEnableNM bool) error {
|
|
if err := r.sLinuxRootFs.DeployNetworkingScripts(rootFs, nics); err != nil {
|
|
return err
|
|
}
|
|
|
|
ver := strings.Split(relInfo.Version, ".")
|
|
iv, err := strconv.ParseInt(ver[0], 10, 0)
|
|
if err == nil && iv < 6 {
|
|
err = r.Centos5DeployNetworkingScripts(rootFs, nics)
|
|
} else {
|
|
err = r.sLinuxRootFs.DeployNetworkingScripts(rootFs, nics)
|
|
}
|
|
if err != nil {
|
|
return err
|
|
}
|
|
// ToServerNics(nics)
|
|
allNics, bondNics := convertNicConfigs(nics)
|
|
if len(bondNics) > 0 {
|
|
err = r.enableBondingModule(rootFs, bondNics)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
mainNic, err := getMainNic(allNics)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
var mainIp string
|
|
if mainNic != nil {
|
|
mainIp = mainNic.Ip
|
|
}
|
|
for i := range allNics {
|
|
nicDesc := allNics[i]
|
|
var cmds strings.Builder
|
|
cmds.WriteString("DEVICE=")
|
|
cmds.WriteString(nicDesc.Name)
|
|
cmds.WriteString("\n")
|
|
cmds.WriteString("NAME=")
|
|
cmds.WriteString(nicDesc.Name)
|
|
cmds.WriteString("\n")
|
|
cmds.WriteString("ONBOOT=yes\n")
|
|
if iv >= 8 || forceEnableNM {
|
|
cmds.WriteString("NM_CONTROLLED=yes\n")
|
|
} else {
|
|
cmds.WriteString("NM_CONTROLLED=no\n")
|
|
}
|
|
cmds.WriteString("USERCTL=no\n")
|
|
if nicDesc.Mtu > 0 {
|
|
cmds.WriteString(fmt.Sprintf("MTU=%d\n", nicDesc.Mtu))
|
|
}
|
|
if len(nicDesc.Mac) > 0 {
|
|
cmds.WriteString("HWADDR=")
|
|
cmds.WriteString(nicDesc.Mac)
|
|
cmds.WriteString("\n")
|
|
cmds.WriteString("MACADDR=")
|
|
cmds.WriteString(nicDesc.Mac)
|
|
cmds.WriteString("\n")
|
|
}
|
|
if len(nicDesc.TeamingSlaves) != 0 {
|
|
cmds.WriteString(`BONDING_OPTS="mode=4 miimon=100"\n`)
|
|
}
|
|
if nicDesc.TeamingMaster != nil {
|
|
cmds.WriteString("BOOTPROTO=none\n")
|
|
cmds.WriteString("MASTER=")
|
|
cmds.WriteString(nicDesc.TeamingMaster.Name)
|
|
cmds.WriteString("\n")
|
|
cmds.WriteString("SLAVE=yes\n")
|
|
} else if nicDesc.Virtual {
|
|
cmds.WriteString("BOOTPROTO=none\n")
|
|
cmds.WriteString("NETMASK=255.255.255.255\n")
|
|
cmds.WriteString("IPADDR=")
|
|
cmds.WriteString(netutils2.PSEUDO_VIP)
|
|
cmds.WriteString("\n")
|
|
} else if nicDesc.Manual {
|
|
netmask := netutils2.Netlen2Mask(int(nicDesc.Masklen))
|
|
cmds.WriteString("BOOTPROTO=none\n")
|
|
cmds.WriteString("NETMASK=")
|
|
cmds.WriteString(netmask)
|
|
cmds.WriteString("\n")
|
|
cmds.WriteString("IPADDR=")
|
|
cmds.WriteString(nicDesc.Ip)
|
|
cmds.WriteString("\n")
|
|
if len(nicDesc.Gateway) > 0 && nicDesc.Ip == mainIp {
|
|
cmds.WriteString("GATEWAY=")
|
|
cmds.WriteString(nicDesc.Gateway)
|
|
cmds.WriteString("\n")
|
|
}
|
|
var routes = make([][]string, 0)
|
|
netutils2.AddNicRoutes(&routes, nicDesc, mainIp, len(nics), privatePrefixes)
|
|
var rtbl strings.Builder
|
|
for _, r := range routes {
|
|
rtbl.WriteString(r[0])
|
|
rtbl.WriteString(" via ")
|
|
rtbl.WriteString(r[1])
|
|
rtbl.WriteString(" dev ")
|
|
rtbl.WriteString(nicDesc.Name)
|
|
rtbl.WriteString("\n")
|
|
}
|
|
rtblStr := rtbl.String()
|
|
if len(rtblStr) > 0 {
|
|
var fn = fmt.Sprintf("/etc/sysconfig/network-scripts/route-%s", nicDesc.Name)
|
|
if err := rootFs.FilePutContents(fn, rtblStr, false, false); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
dnslist := netutils2.GetNicDns(nicDesc)
|
|
if len(dnslist) > 0 {
|
|
cmds.WriteString("PEERDNS=yes\n")
|
|
for i := 0; i < len(dnslist); i++ {
|
|
cmds.WriteString(fmt.Sprintf("DNS%d=%s\n", i+1, dnslist[i]))
|
|
}
|
|
if len(nicDesc.Domain) > 0 {
|
|
cmds.WriteString(fmt.Sprintf("DOMAIN=%s\n", nicDesc.Domain))
|
|
}
|
|
}
|
|
} else {
|
|
cmds.WriteString("BOOTPROTO=dhcp\n")
|
|
}
|
|
var fn = fmt.Sprintf("/etc/sysconfig/network-scripts/ifcfg-%s", nicDesc.Name)
|
|
log.Debugf("%s: %s", fn, cmds.String())
|
|
if err := rootFs.FilePutContents(fn, cmds.String(), false, false); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (r *sRedhatLikeRootFs) DeployStandbyNetworkingScripts(rootFs IDiskPartition, nics, nicsStandby []*types.SServerNic) error {
|
|
if err := r.sLinuxRootFs.DeployStandbyNetworkingScripts(rootFs, nics, nicsStandby); err != nil {
|
|
return err
|
|
}
|
|
for _, nic := range nicsStandby {
|
|
var cmds string
|
|
if len(nic.NicType) == 0 || nic.NicType != "ipmi" {
|
|
cmds += fmt.Sprintf("DEVICE=%s%d\n", NetDevPrefix, nic.Index)
|
|
cmds += fmt.Sprintf("NAME=%s%d\n", NetDevPrefix, nic.Index)
|
|
cmds += fmt.Sprintf("HWADDR=%s\n", nic.Mac)
|
|
cmds += fmt.Sprintf("MACADDR=%s\n", nic.Mac)
|
|
cmds += "ONBOOT=no\n"
|
|
var fn = fmt.Sprintf("/etc/sysconfig/network-scripts/ifcfg-%s%d", NetDevPrefix, nic.Index)
|
|
if err := rootFs.FilePutContents(fn, cmds, false, false); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (r *sRedhatLikeRootFs) getReleaseMajorVersion(drv IRootFsDriver, rootFs IDiskPartition) (int, error) {
|
|
relInfo := drv.GetReleaseInfo(rootFs)
|
|
if len(relInfo.Version) == 0 {
|
|
return 0, fmt.Errorf("release info version is empty")
|
|
}
|
|
log.Infof("Get release info: %#v", relInfo)
|
|
ver, err := strconv.Atoi(string(relInfo.Version[0]))
|
|
if err != nil {
|
|
return 0, fmt.Errorf("Release version %s not start with digit", relInfo.Version)
|
|
}
|
|
return ver, nil
|
|
}
|
|
|
|
func (r *sRedhatLikeRootFs) enableSerialConsole(drv IRootFsDriver, rootFs IDiskPartition, sysInfo *jsonutils.JSONDict) error {
|
|
ver, err := r.getReleaseMajorVersion(drv, rootFs)
|
|
if err != nil {
|
|
return errors.Wrap(err, "Get release major version")
|
|
}
|
|
if ver <= 6 {
|
|
return r.enableSerialConsoleInitCentos(rootFs)
|
|
}
|
|
return r.enableSerialConsoleSystemd(rootFs)
|
|
}
|
|
|
|
func (r *sRedhatLikeRootFs) disableSerialConcole(drv IRootFsDriver, rootFs IDiskPartition) error {
|
|
ver, err := r.getReleaseMajorVersion(drv, rootFs)
|
|
if err != nil {
|
|
return errors.Wrap(err, "Get release major version")
|
|
}
|
|
if ver <= 6 {
|
|
r.disableSerialConsoleInit(rootFs)
|
|
return nil
|
|
}
|
|
r.disableSerialConsoleSystemd(rootFs)
|
|
return nil
|
|
}
|
|
|
|
type SCentosRootFs struct {
|
|
*sRedhatLikeRootFs
|
|
}
|
|
|
|
func NewCentosRootFs(part IDiskPartition) IRootFsDriver {
|
|
return &SCentosRootFs{sRedhatLikeRootFs: newRedhatLikeRootFs(part)}
|
|
}
|
|
|
|
func (c *SCentosRootFs) String() string {
|
|
return "CentosRootFs"
|
|
}
|
|
|
|
func (c *SCentosRootFs) GetName() string {
|
|
return "CentOS"
|
|
}
|
|
|
|
func (c *SCentosRootFs) RootSignatures() []string {
|
|
sig := c.sRedhatLikeRootFs.RootSignatures()
|
|
return append([]string{"/etc/centos-release"}, sig...)
|
|
}
|
|
|
|
func (c *SCentosRootFs) GetReleaseInfo(rootFs IDiskPartition) *deployapi.ReleaseInfo {
|
|
rel, _ := rootFs.FileGetContents("/etc/centos-release", false)
|
|
var version string
|
|
if len(rel) > 0 {
|
|
re := regexp.MustCompile(`^\d+\.\d+`)
|
|
dat := strings.Split(string(rel), " ")
|
|
for _, v := range dat {
|
|
if re.Match([]byte(v)) {
|
|
version = v
|
|
break
|
|
}
|
|
}
|
|
}
|
|
return deployapi.NewReleaseInfo(c.GetName(), version, c.GetArch(rootFs))
|
|
}
|
|
|
|
func (c *SCentosRootFs) DeployNetworkingScripts(rootFs IDiskPartition, nics []*types.SServerNic) error {
|
|
relInfo := c.GetReleaseInfo(rootFs)
|
|
if err := c.sRedhatLikeRootFs.deployNetworkingScripts(rootFs, nics, relInfo, false); err != nil {
|
|
return err
|
|
}
|
|
var udevPath = "/etc/udev/rules.d/"
|
|
var files = []string{"60-net.rules", "75-persistent-net-generator.rules"}
|
|
for _, f := range files {
|
|
sPath := path.Join(udevPath, f)
|
|
if !rootFs.Exists(sPath, false) {
|
|
if err := rootFs.FilePutContents(sPath, "", false, false); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (c *SCentosRootFs) EnableSerialConsole(rootFs IDiskPartition, sysInfo *jsonutils.JSONDict) error {
|
|
return c.enableSerialConsole(c, rootFs, sysInfo)
|
|
}
|
|
|
|
func (c *SCentosRootFs) DisableSerialConsole(rootFs IDiskPartition) error {
|
|
return c.disableSerialConcole(c, rootFs)
|
|
}
|
|
|
|
type SFedoraRootFs struct {
|
|
*sRedhatLikeRootFs
|
|
}
|
|
|
|
func NewFedoraRootFs(part IDiskPartition) IRootFsDriver {
|
|
return &SFedoraRootFs{sRedhatLikeRootFs: newRedhatLikeRootFs(part)}
|
|
}
|
|
|
|
func (c *SFedoraRootFs) String() string {
|
|
return "FedoraRootFs"
|
|
}
|
|
|
|
func (c *SFedoraRootFs) GetName() string {
|
|
return "Fedora"
|
|
}
|
|
|
|
func (c *SFedoraRootFs) RootSignatures() []string {
|
|
sig := c.sRedhatLikeRootFs.RootSignatures()
|
|
return append([]string{"/etc/fedora-release"}, sig...)
|
|
}
|
|
|
|
func (c *SFedoraRootFs) GetReleaseInfo(rootFs IDiskPartition) *deployapi.ReleaseInfo {
|
|
rel, _ := rootFs.FileGetContents("/etc/fedora-release", false)
|
|
var version string
|
|
if len(rel) > 0 {
|
|
re := regexp.MustCompile(`^\d+`)
|
|
dat := strings.Split(string(rel), " ")
|
|
for _, v := range dat {
|
|
if re.Match([]byte(v)) {
|
|
version = v
|
|
break
|
|
}
|
|
}
|
|
}
|
|
return deployapi.NewReleaseInfo(c.GetName(), version, c.GetArch(rootFs))
|
|
}
|
|
|
|
func (c *SFedoraRootFs) DeployNetworkingScripts(rootFs IDiskPartition, nics []*types.SServerNic) error {
|
|
relInfo := c.GetReleaseInfo(rootFs)
|
|
if err := c.sRedhatLikeRootFs.deployNetworkingScripts(rootFs, nics, relInfo, false); err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (c *SFedoraRootFs) EnableSerialConsole(rootFs IDiskPartition, sysInfo *jsonutils.JSONDict) error {
|
|
return c.enableSerialConsole(c, rootFs, sysInfo)
|
|
}
|
|
|
|
func (c *SFedoraRootFs) DisableSerialConsole(rootFs IDiskPartition) error {
|
|
return c.disableSerialConcole(c, rootFs)
|
|
}
|
|
|
|
type SRhelRootFs struct {
|
|
*sRedhatLikeRootFs
|
|
}
|
|
|
|
func NewRhelRootFs(part IDiskPartition) IRootFsDriver {
|
|
return &SRhelRootFs{sRedhatLikeRootFs: newRedhatLikeRootFs(part)}
|
|
}
|
|
|
|
func (d *SRhelRootFs) GetName() string {
|
|
return "RHEL"
|
|
}
|
|
|
|
func (d *SRhelRootFs) String() string {
|
|
return "RhelRootFs"
|
|
}
|
|
|
|
func (d *SRhelRootFs) GetReleaseInfo(rootFs IDiskPartition) *deployapi.ReleaseInfo {
|
|
rel, _ := rootFs.FileGetContents("/etc/redhat-release", false)
|
|
var version string
|
|
if len(rel) > 0 {
|
|
dat := strings.Split(string(rel), " ")
|
|
if len(dat) > 6 {
|
|
version = dat[6]
|
|
}
|
|
}
|
|
return deployapi.NewReleaseInfo(d.GetName(), version, d.GetArch(rootFs))
|
|
}
|
|
|
|
func (d *SRhelRootFs) DeployNetworkingScripts(rootFs IDiskPartition, nics []*types.SServerNic) error {
|
|
relInfo := d.GetReleaseInfo(rootFs)
|
|
if err := d.sRedhatLikeRootFs.deployNetworkingScripts(rootFs, nics, relInfo, false); err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (c *SRhelRootFs) EnableSerialConsole(rootFs IDiskPartition, sysInfo *jsonutils.JSONDict) error {
|
|
return c.enableSerialConsole(c, rootFs, sysInfo)
|
|
}
|
|
|
|
func (c *SRhelRootFs) DisableSerialConsole(rootFs IDiskPartition) error {
|
|
return c.disableSerialConcole(c, rootFs)
|
|
}
|
|
|
|
type SOpenEulerRootFs struct {
|
|
*SCentosRootFs
|
|
}
|
|
|
|
func NewOpenEulerRootFs(part IDiskPartition) IRootFsDriver {
|
|
return &SOpenEulerRootFs{
|
|
SCentosRootFs: NewCentosRootFs(part).(*SCentosRootFs),
|
|
}
|
|
}
|
|
|
|
func (c *SOpenEulerRootFs) String() string {
|
|
return "OpenEulerRootFs"
|
|
}
|
|
|
|
func (c *SOpenEulerRootFs) GetName() string {
|
|
return "OpenEuler"
|
|
}
|
|
|
|
func (c *SOpenEulerRootFs) RootSignatures() []string {
|
|
sig := c.sLinuxRootFs.RootSignatures()
|
|
return append([]string{"/etc/sysconfig/network", "/etc/openEuler-release"}, sig...)
|
|
}
|
|
|
|
func (c *SOpenEulerRootFs) GetReleaseInfo(rootFs IDiskPartition) *deployapi.ReleaseInfo {
|
|
rel, _ := rootFs.FileGetContents("/etc/openEuler-release", false)
|
|
var version string
|
|
if len(rel) > 0 {
|
|
re := regexp.MustCompile(`^\d+\.\d+`)
|
|
dat := strings.Split(string(rel), " ")
|
|
for _, v := range dat {
|
|
if re.Match([]byte(v)) {
|
|
version = v
|
|
break
|
|
}
|
|
}
|
|
}
|
|
version = strings.TrimSpace(version)
|
|
return deployapi.NewReleaseInfo(c.GetName(), version, c.GetArch(rootFs))
|
|
}
|
|
|
|
func (c *SOpenEulerRootFs) DeployNetworkingScripts(rootFs IDiskPartition, nics []*types.SServerNic) error {
|
|
relInfo := c.GetReleaseInfo(rootFs)
|
|
if err := c.sRedhatLikeRootFs.deployNetworkingScripts(rootFs, nics, relInfo, false); err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
}
|
|
|
|
type SGentooRootFs struct {
|
|
*sLinuxRootFs
|
|
}
|
|
|
|
func NewGentooRootFs(part IDiskPartition) IRootFsDriver {
|
|
return &SGentooRootFs{sLinuxRootFs: newLinuxRootFs(part)}
|
|
}
|
|
|
|
func (d *SGentooRootFs) GetName() string {
|
|
return "Gentoo"
|
|
}
|
|
|
|
func (d *SGentooRootFs) String() string {
|
|
return "GentooRootFs"
|
|
}
|
|
|
|
func (d *SGentooRootFs) RootSignatures() []string {
|
|
sig := d.sLinuxRootFs.RootSignatures()
|
|
if sig != nil {
|
|
return append(sig, "/etc/gentoo-release")
|
|
} else {
|
|
return []string{"/etc/gentoo-release"}
|
|
}
|
|
}
|
|
|
|
func (d *SGentooRootFs) DeployHostname(rootFs IDiskPartition, hn, domain string) error {
|
|
spath := "/etc/conf.d/hostname"
|
|
content := fmt.Sprintf(`hostname="%s"\n`, hn)
|
|
return rootFs.FilePutContents(spath, content, false, false)
|
|
}
|
|
|
|
func (l *SGentooRootFs) DeployNetworkingScripts(rootFs IDiskPartition, nics []*types.SServerNic) error {
|
|
if err := l.sLinuxRootFs.DeployNetworkingScripts(rootFs, nics); err != nil {
|
|
return err
|
|
}
|
|
|
|
var (
|
|
fn = "/etc/conf.d/net"
|
|
cmds = ""
|
|
)
|
|
|
|
// Ref https://wiki.gentoo.org/wiki/Netifrc
|
|
for _, nic := range nics {
|
|
nicIndex := nic.Index
|
|
if nic.Virtual {
|
|
cmds += fmt.Sprintf(`config_%s%d="`, NetDevPrefix, nicIndex)
|
|
cmds += fmt.Sprintf("%s netmask 255.255.255.255", netutils2.PSEUDO_VIP)
|
|
cmds += `"\n`
|
|
} else {
|
|
cmds += fmt.Sprintf(`config_%s%d="dhcp"\n`, NetDevPrefix, nicIndex)
|
|
}
|
|
if nic.Mtu > 0 {
|
|
cmds += fmt.Sprintf(`mtu_%s%d="%d"\n`, NetDevPrefix, nicIndex, nic.Mtu)
|
|
}
|
|
}
|
|
if err := rootFs.FilePutContents(fn, cmds, false, false); err != nil {
|
|
return err
|
|
}
|
|
for _, nic := range nics {
|
|
nicIndex := nic.Index
|
|
netname := fmt.Sprintf("net.%s%d", NetDevPrefix, nicIndex)
|
|
procutils.NewCommand("ln", "-s", "net.lo",
|
|
fmt.Sprintf("%s/etc/init.d/%s", rootFs.GetMountPath(), netname)).Run()
|
|
procutils.NewCommand("chroot",
|
|
rootFs.GetMountPath(), "rc-update", "add", netname, "default").Run()
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (d *SGentooRootFs) GetReleaseInfo(rootFs IDiskPartition) *deployapi.ReleaseInfo {
|
|
return &deployapi.ReleaseInfo{
|
|
Distro: "Gentoo",
|
|
Arch: d.GetArch(rootFs),
|
|
}
|
|
}
|
|
|
|
type SArchLinuxRootFs struct {
|
|
*sLinuxRootFs
|
|
}
|
|
|
|
func NewArchLinuxRootFs(part IDiskPartition) IRootFsDriver {
|
|
return &SArchLinuxRootFs{sLinuxRootFs: newLinuxRootFs(part)}
|
|
}
|
|
|
|
func (d *SArchLinuxRootFs) GetName() string {
|
|
return "ArchLinux"
|
|
}
|
|
|
|
func (d *SArchLinuxRootFs) String() string {
|
|
return "ArchLinuxRootFs"
|
|
}
|
|
|
|
func (d *SArchLinuxRootFs) RootSignatures() []string {
|
|
sig := d.sLinuxRootFs.RootSignatures()
|
|
if sig != nil {
|
|
return append(sig, "/etc/arch-release")
|
|
} else {
|
|
return []string{"/etc/arch-release"}
|
|
}
|
|
}
|
|
|
|
func (d *SArchLinuxRootFs) DeployHostname(rootFs IDiskPartition, hn, domain string) error {
|
|
return rootFs.FilePutContents("/etc/hostname", hn, false, false)
|
|
}
|
|
|
|
func (d *SArchLinuxRootFs) GetReleaseInfo(rootFs IDiskPartition) *deployapi.ReleaseInfo {
|
|
return &deployapi.ReleaseInfo{
|
|
Distro: "ArchLinux",
|
|
Arch: d.GetArch(rootFs),
|
|
}
|
|
}
|
|
|
|
type SOpenWrtRootFs struct {
|
|
*sLinuxRootFs
|
|
}
|
|
|
|
func NewOpenWrtRootFs(part IDiskPartition) IRootFsDriver {
|
|
return &SOpenWrtRootFs{sLinuxRootFs: newLinuxRootFs(part)}
|
|
}
|
|
|
|
func (d *SOpenWrtRootFs) GetName() string {
|
|
return "OpenWrt"
|
|
}
|
|
|
|
func (d *SOpenWrtRootFs) String() string {
|
|
return "OpenWrtRootFs"
|
|
}
|
|
|
|
func (d *SOpenWrtRootFs) RootSignatures() []string {
|
|
return []string{"/bin", "/etc/", "/lib", "/sbin", "/overlay", "/etc/openwrt_release", "/etc/openwrt_version"}
|
|
}
|
|
func (d *SOpenWrtRootFs) featureBoardConfig(rootFs IDiskPartition) bool {
|
|
if rootFs.Exists("/etc/board.d", false) {
|
|
return true
|
|
}
|
|
return false
|
|
}
|
|
|
|
func (d *SOpenWrtRootFs) putBoardConfig(rootFs IDiskPartition, f, c string) error {
|
|
if err := rootFs.FilePutContents(f, c, false, false); err != nil {
|
|
return err
|
|
}
|
|
if err := rootFs.Chmod(f, 0755, false); err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (d *SOpenWrtRootFs) DeployPublicKey(rootFs IDiskPartition, selUsr string, pubkeys *deployapi.SSHKeys) error {
|
|
if selUsr == "root" && rootFs.Exists("/etc/dropbear", false) {
|
|
var (
|
|
authFile = "/etc/dropbear/authorized_keys"
|
|
uid = 0
|
|
gid = 0
|
|
replace = false
|
|
)
|
|
return deployAuthorizedKeys(rootFs, authFile, uid, gid, pubkeys, replace)
|
|
}
|
|
return d.sLinuxRootFs.DeployPublicKey(rootFs, selUsr, pubkeys)
|
|
}
|
|
|
|
func (d *SOpenWrtRootFs) DeployHostname(rootFs IDiskPartition, hn, domain string) error {
|
|
if d.featureBoardConfig(rootFs) {
|
|
f := "/etc/board.d/00-00-onecloud-hostname"
|
|
c := fmt.Sprintf(`. /lib/functions/uci-defaults.sh
|
|
board_config_update
|
|
ucidef_set_hostname '%s'
|
|
board_config_flush
|
|
exit 0
|
|
`, hn)
|
|
return d.putBoardConfig(rootFs, f, c)
|
|
}
|
|
|
|
spath := "/etc/config/system"
|
|
if !rootFs.Exists(spath, false) {
|
|
return nil
|
|
}
|
|
bcont, err := rootFs.FileGetContents(spath, false)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
cont := string(bcont)
|
|
re := regexp.MustCompile("option hostname [^\n]+")
|
|
cont = re.ReplaceAllString(cont, fmt.Sprintf("option hostname %s", hn))
|
|
return rootFs.FilePutContents(spath, cont, false, false)
|
|
}
|
|
|
|
func (d *SOpenWrtRootFs) DeployNetworkingScripts(rootFs IDiskPartition, nics []*types.SServerNic) error {
|
|
if d.featureBoardConfig(rootFs) {
|
|
macs := ""
|
|
for _, nic := range nics {
|
|
macs = "," + nic.Mac
|
|
}
|
|
f := "/etc/board.d/00-01-onecloud-network"
|
|
c := fmt.Sprintf(`. /lib/functions/uci-defaults.sh
|
|
[ -d /sys/class/net ] || exit 0
|
|
|
|
board_config_update
|
|
macs='%s'
|
|
i=0
|
|
|
|
oc_set_ifname() {
|
|
local net="$1"; shift
|
|
local ifname="$1"; shift
|
|
|
|
if type ucidef_set_interface &>/dev/null; then
|
|
ucidef_set_interface "$net" ifname "$ifname" protocol dhcp
|
|
elif type ucidef_set_interface_raw &>/dev/null; then
|
|
ucidef_set_interface_raw "$net" "$ifname" "dhcp"
|
|
else
|
|
echo "no ucidef function to do network ifname config" >&2
|
|
exit 0
|
|
fi
|
|
}
|
|
|
|
for ifname in $(ls /sys/class/net/); do
|
|
p="/sys/class/net/$ifname"
|
|
mac="$(cat "$p/address")"
|
|
if [ "${macs#*,$mac}" != "$macs" ]; then
|
|
oc_set_ifname "lan$i" "$ifname"
|
|
i="$(($i + 1))"
|
|
fi
|
|
done
|
|
|
|
board_config_flush
|
|
exit 0
|
|
`, macs)
|
|
return d.putBoardConfig(rootFs, f, c)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (d *SOpenWrtRootFs) GetReleaseInfo(rootFs IDiskPartition) *deployapi.ReleaseInfo {
|
|
ver, _ := rootFs.FileGetContents("/etc/openwrt_version", false)
|
|
return &deployapi.ReleaseInfo{
|
|
Distro: "OpenWrt",
|
|
Version: string(ver),
|
|
Arch: d.GetArch(rootFs),
|
|
}
|
|
}
|
|
|
|
type SCoreOsRootFs struct {
|
|
*sGuestRootFsDriver
|
|
config *coreosutils.SCloudConfig
|
|
}
|
|
|
|
func NewCoreOsRootFs(part IDiskPartition) IRootFsDriver {
|
|
return &SCoreOsRootFs{sGuestRootFsDriver: newGuestRootFsDriver(part)}
|
|
}
|
|
|
|
func (d *SCoreOsRootFs) GetName() string {
|
|
return "CoreOs"
|
|
}
|
|
|
|
func (d *SCoreOsRootFs) String() string {
|
|
return "CoreOsRootFs"
|
|
}
|
|
|
|
func (d *SCoreOsRootFs) GetOs() string {
|
|
return "Linux"
|
|
}
|
|
|
|
func (d *SCoreOsRootFs) GetReleaseInfo(rootFs IDiskPartition) *deployapi.ReleaseInfo {
|
|
return &deployapi.ReleaseInfo{
|
|
Distro: "CoreOS",
|
|
Version: "stable",
|
|
}
|
|
}
|
|
|
|
func (d *SCoreOsRootFs) RootSignatures() []string {
|
|
return []string{"cloud-config.yml"}
|
|
}
|
|
|
|
func (d *SCoreOsRootFs) PrepareFsForTemplate(rootFs IDiskPartition) error {
|
|
return nil
|
|
}
|
|
|
|
func (d *SCoreOsRootFs) GetConfig() *coreosutils.SCloudConfig {
|
|
if d.config == nil {
|
|
d.config = coreosutils.NewCloudConfig()
|
|
d.config.YunionInit()
|
|
d.config.SetTimezone("Asia/Shanghai")
|
|
}
|
|
return d.config
|
|
}
|
|
|
|
func (d *SCoreOsRootFs) DeployHostname(rootFs IDiskPartition, hn, domain string) error {
|
|
d.GetConfig().SetHostname(hn)
|
|
return nil
|
|
}
|
|
|
|
func (d *SCoreOsRootFs) DeployPublicKey(rootFs IDiskPartition, selUsr string, pubkeys *deployapi.SSHKeys) error {
|
|
return nil
|
|
}
|
|
|
|
func (d *SCoreOsRootFs) DeployHosts(rootFs IDiskPartition, hostname, domain string, ips []string) error {
|
|
d.GetConfig().SetEtcHosts("localhost")
|
|
return nil
|
|
}
|
|
|
|
func (d *SCoreOsRootFs) DeployNetworkingScripts(rootFs IDiskPartition, nics []*types.SServerNic) error {
|
|
for _, nic := range nics {
|
|
name := fmt.Sprintf("%s%d", NetDevPrefix, nic.Index)
|
|
cont := "[Match]\n"
|
|
cont += "Name=" + name + "\n"
|
|
cont += "\n[Network]\n"
|
|
cont += "DHCP=yes\n"
|
|
if nic.Mtu > 0 {
|
|
cont += "\n[Link]\n"
|
|
cont += fmt.Sprintf("MTUBytes=%d\n", nic.Mtu)
|
|
}
|
|
runtime := true
|
|
d.GetConfig().AddUnits("00-dhcp-"+name+".network", nil, nil, &runtime, cont, "", nil)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (d *SCoreOsRootFs) DeployFstabScripts(rootFs IDiskPartition, disks []*deployapi.Disk) error {
|
|
dataDiskIdx := 0
|
|
for i := 1; i < len(disks); i++ {
|
|
dev := fmt.Sprintf("UUID=%s", disks[i].DiskId)
|
|
fs := disks[i].Fs
|
|
if len(fs) > 0 {
|
|
if fs == "swap" {
|
|
d.GetConfig().AddSwap(dev)
|
|
} else {
|
|
mtPath := disks[i].Mountpoint
|
|
if len(mtPath) == 0 {
|
|
mtPath = "/data"
|
|
if dataDiskIdx > 0 {
|
|
mtPath += fmt.Sprintf("%d", dataDiskIdx)
|
|
}
|
|
dataDiskIdx += 1
|
|
}
|
|
d.GetConfig().AddPartition(dev, mtPath, fs)
|
|
}
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (d *SCoreOsRootFs) ChangeUserPasswd(rootFs IDiskPartition, account, gid, publicKey, password string) (string, error) {
|
|
keys := []string{}
|
|
if len(publicKey) > 0 {
|
|
keys = append(keys, publicKey)
|
|
}
|
|
d.GetConfig().AddUser("core", password, keys, false)
|
|
if len(publicKey) > 0 {
|
|
return seclib2.EncryptBase64(publicKey, password)
|
|
} else {
|
|
return utils.EncryptAESBase64(gid, password)
|
|
}
|
|
}
|
|
|
|
func (d *SCoreOsRootFs) GetLoginAccount(rootFs IDiskPartition, user string, defaultRootUser bool, windowsDefaultAdminUser bool) (string, error) {
|
|
return "core", nil
|
|
}
|
|
|
|
func (d *SCoreOsRootFs) DeployFiles(deploys []*deployapi.DeployContent) error {
|
|
for _, deploy := range deploys {
|
|
d.GetConfig().AddWriteFile(deploy.Path, deploy.Content, "", "", false)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (d *SCoreOsRootFs) CommitChanges(IDiskPartition) error {
|
|
ocont, err := d.rootFs.FileGetContents("/cloud-config.yml", false)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
ocfg := coreosutils.NewCloudConfig()
|
|
err = yaml.Unmarshal(ocont, ocfg)
|
|
if err != nil {
|
|
log.Errorln(err)
|
|
}
|
|
conf := d.GetConfig()
|
|
if len(ocfg.Users) > 0 {
|
|
for _, u := range ocfg.Users {
|
|
if conf.HasUser(u.Name) {
|
|
conf.AddUser(u.Name, u.Passwd, u.SshAuthorizedKeys, true)
|
|
}
|
|
}
|
|
}
|
|
if len(ocfg.WriteFiles) > 0 {
|
|
for _, f := range ocfg.WriteFiles {
|
|
if !conf.HasWriteFile(f.Path) {
|
|
conf.AddWriteFile(f.Path, f.Content, f.Permissions, f.Owner, f.Encoding == "base64")
|
|
}
|
|
}
|
|
}
|
|
return d.rootFs.FilePutContents("/cloud-config.yml", conf.String(), false, false)
|
|
}
|