diff --git a/pkg/util/iproute2/address.go b/pkg/util/iproute2/address_linux.go similarity index 100% rename from pkg/util/iproute2/address.go rename to pkg/util/iproute2/address_linux.go diff --git a/pkg/util/iproute2/address_others.go b/pkg/util/iproute2/address_others.go new file mode 100644 index 0000000000..0f52d06346 --- /dev/null +++ b/pkg/util/iproute2/address_others.go @@ -0,0 +1,85 @@ +// 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. + +//go:build !linux +// +build !linux + +package iproute2 + +import ( + "github.com/vishvananda/netlink" +) + +type Address struct { + *Link + + addrBad bool + addrs []*netlink.Addr + + testcb func() +} + +func nop() {} + +func NewAddress(ifname string, addresses ...string) *Address { + l := NewLink(ifname) + r := &Address{ + Link: l, + testcb: nop, + } + + addrs := make([]*netlink.Addr, len(addresses)) + for i, address := range addresses { + addr, err := netlink.ParseAddr(address) + if err != nil { + r.addrBad = true + r.addErr(err, "parse addr") + continue + } + addrs[i] = addr + } + if !r.addrBad { + r.addrs = addrs + } + return r +} + +func (address *Address) Exact() *Address { + return nil +} + +func (address *Address) link() (link netlink.Link, ok bool) { + if address.addrBad { + return + } + link = address.Link.link + if link != nil { + ok = true + } + return +} + +func (address *Address) Add() *Address { + link, ok := address.link() + if !ok { + return address + } + for _, addr := range address.addrs { + err := netlink.AddrReplace(link, addr) + if err != nil { + address.addErr(err, "Add: AddrReplace %s ", addr) + } + } + return address +} diff --git a/pkg/util/iproute2/route.go b/pkg/util/iproute2/route_linux.go similarity index 100% rename from pkg/util/iproute2/route.go rename to pkg/util/iproute2/route_linux.go diff --git a/pkg/util/iproute2/route_others.go b/pkg/util/iproute2/route_others.go new file mode 100644 index 0000000000..6927fae52a --- /dev/null +++ b/pkg/util/iproute2/route_others.go @@ -0,0 +1,191 @@ +// 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. + +//go:build !linux +// +build !linux + +package iproute2 + +import ( + "net" + + "github.com/vishvananda/netlink" + + "yunion.io/x/pkg/errors" +) + +const ( + errBadIP = errors.Error("bad ip") +) + +type RouteSpec = netlink.Route + +type Route struct { + *Link +} + +func NewRoute(ifname string) *Route { + l := NewLink(ifname) + route := &Route{ + Link: l, + } + return route +} + +func (route *Route) link() (link netlink.Link, ok bool) { + link = route.Link.link + if link != nil { + ok = true + } + return +} + +func (route *Route) List4() ([]RouteSpec, error) { + return nil, errors.ErrNotImplemented +} + +func (route *Route) AddByIPNet(ipnet *net.IPNet, gw net.IP) *Route { + r := RouteSpec{ + Dst: ipnet, + } + if len(gw) > 0 { + r.Gw = gw + } + return route.AddByRouteSpec(r) +} + +func (route *Route) AddByCidr(cidr string, gwStr string) *Route { + dst, gw, err := route.parseCidr(cidr, gwStr) + if err != nil { + route.addErr2(err) + return route + } + + return route.AddByIPNet(dst, gw) +} + +func (route *Route) AddByRouteSpec(r RouteSpec) *Route { + _, ok := route.link() + if !ok { + return route + } + return route +} + +func (route *Route) parseCidr(cidr, gwStr string) (dst *net.IPNet, gw net.IP, err error) { + if _, dst, err = net.ParseCIDR(cidr); err != nil { + err = errors.Wrap(err, "parse cidr") + return + } + + if gwStr != "" { + gw = net.ParseIP(gwStr) + if len(gw) == 0 { + err = errors.Wrapf(errBadIP, "gwStr: %s", gwStr) + return + } + } + return +} + +func (route *Route) parse(netStr, maskStr, gwStr string) (ip net.IP, mask net.IPMask, gw net.IP, err error) { + if ip = net.ParseIP(netStr); len(ip) == 0 { + err = errors.Wrapf(errBadIP, "netStr %s", netStr) + return + } + if maskIp := net.ParseIP(maskStr); len(maskIp) == 0 { + err = errors.Wrapf(errBadIP, "maskStr %s", maskStr) + return + } else { + if ip := maskIp.To4(); len(ip) > 0 { + maskIp = ip + } + mask = net.IPMask(maskIp) + ones, bits := mask.Size() + if ones == 0 && bits == 0 { + err = errors.Wrapf(errBadIP, "bad mask %s", maskStr) + return + } + } + if gwStr != "" { + if gw = net.ParseIP(gwStr); len(gw) == 0 { + err = errors.Wrapf(errBadIP, "gwStr %s", gwStr) + return + } + } + return +} + +func (route *Route) Add(netStr, maskStr, gwStr string) *Route { + var ( + ip net.IP + mask net.IPMask + gw net.IP + ) + + ip, mask, gw, err := route.parse(netStr, maskStr, gwStr) + if err != nil { + route.addErr2(err) + } + + ipnet := &net.IPNet{ + IP: ip, + Mask: mask, + } + return route.AddByIPNet(ipnet, gw) +} + +func (route *Route) Del(netStr, maskStr string) *Route { + ip, mask, _, err := route.parse(netStr, maskStr, "") + if err != nil { + route.addErr2(err) + return route + } + ipnet := &net.IPNet{ + IP: ip, + Mask: mask, + } + return route.DelByIPNet(ipnet) +} + +func (route *Route) DelByCidr(cidr string) *Route { + dst, _, err := route.parseCidr(cidr, "") + if err != nil { + route.addErr2(err) + return route + } + + return route.DelByIPNet(dst) +} + +func (route *Route) DelByIPNet(ipnet *net.IPNet) *Route { + link, ok := route.link() + if !ok { + return route + } + + r := &netlink.Route{ + LinkIndex: link.Attrs().Index, + Dst: ipnet, + } + if err := netlink.RouteDel(r); err != nil { + route.addErr(err, "RouteDel %s", r) + return route + } + return route +} + +func RouteGetByDst(dstStr string) ([]RouteSpec, error) { + return nil, errors.ErrNotImplemented +} diff --git a/pkg/util/netutils2/netutils_others.go b/pkg/util/netutils2/netutils_others.go index 5717879d77..dcf9d355bc 100644 --- a/pkg/util/netutils2/netutils_others.go +++ b/pkg/util/netutils2/netutils_others.go @@ -17,6 +17,23 @@ package netutils2 +import ( + "net" + + "yunion.io/x/pkg/errors" + + "yunion.io/x/onecloud/pkg/util/iproute2" +) + func (n *SNetInterface) GetAddresses() [][]string { return nil } + +func (n *SNetInterface) GetRouteSpecs() []iproute2.RouteSpec { + return []iproute2.RouteSpec{} +} + +func DefaultSrcIpDev() (srcIp net.IP, ifname string, err error) { + err = errors.ErrNotImplemented + return +} diff --git a/pkg/util/procutils/remote_readdir.go b/pkg/util/procutils/remote_readdir.go index c9bb276e0f..5bcc6e76fc 100644 --- a/pkg/util/procutils/remote_readdir.go +++ b/pkg/util/procutils/remote_readdir.go @@ -18,6 +18,7 @@ import ( "fmt" "os" "regexp" + "runtime" "strconv" "strings" @@ -36,6 +37,7 @@ func split(line string) []string { func parseLsLine(line string) (os.FileInfo, error) { // drwxr-xr-x. 7 1000 20 4096 2022-02-16 08:10:21.660000000 +0800 .vim // -rw-------. 1 0 0 23658 2022-04-21 17:31:04.320995359 +0800 .viminfo + // lrwxr-xr-x 1 501 80 31 2022-11-02 21:01:10.000000000 +0800 zstdmt -> ../Cellar/zstd/1.5.2/bin/zstdmt line = strings.TrimSpace(line) if len(line) < 10 { return nil, errors.Error("invalid ls line: too short") @@ -79,7 +81,14 @@ func parseLsLine(line string) (os.FileInfo, error) { } func RemoteReadDir(dirname string) ([]os.FileInfo, error) { - output, err := NewRemoteCommandAsFarAsPossible("ls", "-la1n", "--full-time", dirname).Output() + args := []string{} + switch runtime.GOOS { + case "darwin": + args = []string{"-la1n", "-D", `%Y-%m-%d %H:%M:%S.000000000 %z`, dirname} + default: + args = []string{"-la1n", "--full-time", dirname} + } + output, err := NewRemoteCommandAsFarAsPossible("ls", args...).Output() if err != nil { if strings.Contains(strings.ToLower(string(output)), "no such file or directory") { return nil, os.ErrNotExist @@ -91,7 +100,7 @@ func RemoteReadDir(dirname string) ([]os.FileInfo, error) { for _, line := range lines { f, err := parseLsLine(line) if err != nil { - // log.Errorf("parseLsLine %s fail %s", line, err) + //log.Errorf("parseLsLine %s fail %s", line, err) } else { files = append(files, f) } diff --git a/pkg/util/procutils/remote_stat.go b/pkg/util/procutils/remote_stat.go index 18845fac7c..57b9fd42ad 100644 --- a/pkg/util/procutils/remote_stat.go +++ b/pkg/util/procutils/remote_stat.go @@ -16,6 +16,7 @@ package procutils import ( "os" + "runtime" "strings" "time" @@ -58,7 +59,14 @@ func (s *sFileStat) Sys() interface{} { } func RemoteStat(filename string) (os.FileInfo, error) { - output, err := NewRemoteCommandAsFarAsPossible("stat", "-c", `{"file_size":%s,"file_name":"%n","file_type":"%F"}`, filename).Output() + args := []string{} + switch runtime.GOOS { + case "darwin": + args = []string{"-f", `{"file_size":%z,"file_name":"%N","file_type":"%T"}`, filename} + default: + args = []string{"-c", `{"file_size":%s,"file_name":"%n","file_type":"%F"}`, filename} + } + output, err := NewRemoteCommandAsFarAsPossible("stat", args...).Output() if err != nil { if strings.Contains(strings.ToLower(string(output)), "no such file or directory") { return nil, os.ErrNotExist diff --git a/pkg/util/qemuimg/qemuimg.go b/pkg/util/qemuimg/qemuimg.go index 565c453ebb..6f39028f28 100644 --- a/pkg/util/qemuimg/qemuimg.go +++ b/pkg/util/qemuimg/qemuimg.go @@ -17,6 +17,7 @@ package qemuimg import ( "fmt" "os" + "runtime" "strconv" "strings" @@ -337,6 +338,10 @@ func convertOther(srcInfo, destInfo SImageInfo, compact bool, workerOpions []str cmdline = append(cmdline, srcInfo.Path, destInfo.Path) log.Infof("XXXX qemu-img command: %s", cmdline) cmd := procutils.NewRemoteCommandAsFarAsPossible("ionice", cmdline...) + if runtime.GOOS == "darwin" { + cmdline = cmdline[2:] + cmd = procutils.NewRemoteCommandAsFarAsPossible(cmdline[0], cmdline[1:]...) + } output, err := cmd.Output() if err != nil { os.Remove(destInfo.Path) @@ -405,6 +410,10 @@ func convertEncrypt(srcInfo, destInfo SImageInfo, compact bool, workerOpions []s cmdline = append(cmdline, "--target-image-opts", destInfo.ImageOptions()) cmdline = append(cmdline, "-n") cmd := procutils.NewRemoteCommandAsFarAsPossible("ionice", cmdline...) + if runtime.GOOS == "darwin" { + cmdline = cmdline[2:] + cmd = procutils.NewRemoteCommandAsFarAsPossible(cmdline[0], cmdline[1:]...) + } output, err := cmd.Output() log.Infof("XXXX qemu-img convert command: %s output: %s", cmdline, output) if err != nil { @@ -603,6 +612,10 @@ func (img *SQemuImage) create(sizeMB int, format qemuimgfmt.TImageFormat, option args = append(args, fmt.Sprintf("%dM", sizeMB)) } cmd := procutils.NewRemoteCommandAsFarAsPossible("ionice", args...) + if runtime.GOOS == "darwin" { + args = args[2:] + cmd = procutils.NewRemoteCommandAsFarAsPossible(args[0], args[1:]...) + } output, err := cmd.Output() if err != nil { return errors.Wrapf(err, "create image failed: %s", output) @@ -728,6 +741,10 @@ func (img *SQemuImage) Resize(sizeMB int) error { args = append(args, "--image-opts", encInfo.ImageOptions()) args = append(args, fmt.Sprintf("%dM", sizeMB)) cmd := procutils.NewRemoteCommandAsFarAsPossible("ionice", args...) + if runtime.GOOS == "darwin" { + args = args[2:] + cmd = procutils.NewRemoteCommandAsFarAsPossible(args[0], args[1:]...) + } output, err := cmd.Output() if err != nil { return errors.Wrapf(err, "resize: %s", string(output)) @@ -759,6 +776,10 @@ func (img *SQemuImage) Rebase(backPath string, force bool) error { } args = append(args, "-b", backPath) cmd := procutils.NewRemoteCommandAsFarAsPossible("ionice", args...) + if runtime.GOOS == "darwin" { + args = args[2:] + cmd = procutils.NewRemoteCommandAsFarAsPossible(args[0], args[1:]...) + } output, err := cmd.Output() if err != nil { return errors.Wrapf(err, "rebase %s", string(output)) @@ -827,6 +848,10 @@ func (img *SQemuImage) Check() error { } args = append(args, "--image-opts", info.ImageOptions()) cmd := procutils.NewRemoteCommandAsFarAsPossible("ionice", args...) + if runtime.GOOS == "darwin" { + args = args[2:] + cmd = procutils.NewRemoteCommandAsFarAsPossible(args[0], args[1:]...) + } output, err := cmd.Output() if err != nil { return errors.Wrapf(err, "check: %s", string(output)) diff --git a/pkg/util/qemutils/qemutils.go b/pkg/util/qemutils/qemutils.go index 4ed5792ccb..207193e07c 100644 --- a/pkg/util/qemutils/qemutils.go +++ b/pkg/util/qemutils/qemutils.go @@ -28,8 +28,9 @@ import ( ) const ( - USER_LOCAL_BIN = "/usr/local/bin" - USER_BIN = "/usr/bin" + USR_LOCAL_BIN = "/usr/local/bin" + HOMEBREW_BIN = "/opt/homebrew/bin/" + USR_BIN = "/usr/bin" ) var qemuSystemCmd = "qemu-system-x86_64" @@ -66,13 +67,19 @@ func getQemuCmdByVersion(cmd, version string) string { log.Errorf("stat %s: %s", p, err) } cmd = cmd + "_" + version - p = path.Join(USER_LOCAL_BIN, cmd) + p = path.Join(USR_LOCAL_BIN, cmd) if _, err := procutils.RemoteStat(p); err == nil { return p } else { log.Errorf("stat %s: %s", p, err) } - p = path.Join(USER_BIN, cmd) + p = path.Join(USR_BIN, cmd) + if _, err := procutils.RemoteStat(p); err == nil { + return p + } else { + log.Errorf("stat %s: %s", p, err) + } + p = path.Join(HOMEBREW_BIN, cmd) if _, err := procutils.RemoteStat(p); err == nil { return p } else { @@ -120,7 +127,7 @@ func getQemuDefaultCmd(cmd string) string { } cmds := make([]string, 0) - for _, dir := range []string{USER_LOCAL_BIN, USER_BIN} { + for _, dir := range []string{USR_LOCAL_BIN, USR_BIN, HOMEBREW_BIN} { if files, err := procutils.RemoteReadDir(dir); err == nil { for i := 0; i < len(files); i++ { if strings.HasPrefix(files[i].Name(), cmd) {