diff --git a/cmd/climc/shell/compute/servernetworks.go b/cmd/climc/shell/compute/servernetworks.go index d5e96c9fa5..ef40516195 100644 --- a/cmd/climc/shell/compute/servernetworks.go +++ b/cmd/climc/shell/compute/servernetworks.go @@ -94,6 +94,7 @@ func init() { Driver string `help:"Driver model of vNIC" choices:"virtio|e1000|vmxnet3|rtl8139"` Index int64 `help:"Index of NIC" default:"-1"` Ifname string `help:"Interface name of vNIC on host"` + Default bool `help:"is default nic?"` } R(&ServerNetworkUpdateOptions{}, "server-network-update", "Update server network settings", func(s *mcclient.ClientSession, args *ServerNetworkUpdateOptions) error { params := jsonutils.NewDict() @@ -106,6 +107,9 @@ func init() { if len(args.Ifname) > 0 { params.Add(jsonutils.NewString(args.Ifname), "ifname") } + if args.Default { + params.Add(jsonutils.JSONTrue, "is_default") + } if params.Size() == 0 { return InvalidUpdateError() } diff --git a/pkg/apigateway/handler/resource.go b/pkg/apigateway/handler/resource.go index 2dac0cc683..c212f1ddbc 100644 --- a/pkg/apigateway/handler/resource.go +++ b/pkg/apigateway/handler/resource.go @@ -644,12 +644,13 @@ func (f *ResourceHandlers) updateJointHandler(ctx context.Context, w http.Respon session := req.Session() module := req.Mod1() module2 := req.Mod2() + query := req.Query() body := req.Body() jmod, e := modulebase.GetJointModule2(session, module, module2) var obj jsonutils.JSONObject if e == nil { // update joint - obj, e = jmod.Update(session, req.ResID(), req.ResID2(), nil, body) + obj, e = jmod.Update(session, req.ResID(), req.ResID2(), query, body) } else { // update in context obj, e = module2.PutInContext(session, req.ResID2(), body, module, req.ResID()) } @@ -669,12 +670,13 @@ func (f *ResourceHandlers) patchJointHandler(ctx context.Context, w http.Respons session := req.Session() module := req.Mod1() module2 := req.Mod2() + query := req.Query() body := req.Body() jmod, e := modulebase.GetJointModule2(session, module, module2) var obj jsonutils.JSONObject if e == nil { // update joint - obj, e = jmod.Patch(session, req.ResID(), req.ResID2(), nil, body) + obj, e = jmod.Patch(session, req.ResID(), req.ResID2(), query, body) } else { // update in context obj, e = module2.PatchInContext(session, req.ResID2(), body, module, req.ResID()) } diff --git a/pkg/compute/models/guest_actions.go b/pkg/compute/models/guest_actions.go index b4d1d0e695..0e9df58652 100644 --- a/pkg/compute/models/guest_actions.go +++ b/pkg/compute/models/guest_actions.go @@ -2564,6 +2564,29 @@ func (self *SGuest) PerformDetachnetwork( return nil, httperrors.NewMissingParameterError("net_id") } + removeNics := make(map[string]*SGuestnetwork) + for i := range gns { + removeNics[gns[i].MacAddr] = &gns[i] + } + + slaveNics, err := self.GetSlaveNetworks() + if err != nil { + return nil, errors.Wrap(err, "GetSlaveNetworks") + } + + for i := range slaveNics { + teamMac := slaveNics[i].TeamWith + if len(teamMac) > 0 { + if _, ok := removeNics[teamMac]; ok { + // to remove slave's master NIC, should also remove slave itself + if _, ok2 := removeNics[slaveNics[i].MacAddr]; !ok2 { + // otherwise, report the error + return nil, errors.Wrap(errors.ErrInvalidStatus, "") + } + } + } + } + err = self.detachNetworks(ctx, userCred, gns, input.Reserve) if err != nil { return nil, errors.Wrap(err, "detachNetworks") @@ -2599,6 +2622,9 @@ func (guest *SGuest) fixDefaultGateway(ctx context.Context, userCred mcclient.To nicList := netutils2.SNicInfoList{} nics, _ := guest.GetNetworks("") for i := range nics { + if nics[i].Virtual || len(nics[i].TeamWith) > 0 { + continue + } net, _ := nics[i].GetNetwork() if net != nil { nicList = nicList.Add(nics[i].IpAddr, nics[i].MacAddr, net.GuestGateway) diff --git a/pkg/compute/models/guestnetworks.go b/pkg/compute/models/guestnetworks.go index 6d56ee4bff..01dde8acaf 100644 --- a/pkg/compute/models/guestnetworks.go +++ b/pkg/compute/models/guestnetworks.go @@ -812,6 +812,9 @@ func (gn *SGuestnetwork) ValidateUpdateData( } } if input.IsDefault != nil && *input.IsDefault { + if gn.Virtual || len(gn.TeamWith) > 0 { + return input, errors.Wrap(httperrors.ErrInvalidStatus, "cannot set virtual/slave interface as default") + } net, err := gn.GetNetwork() if err != nil { return input, errors.Wrapf(err, "GetNetwork") diff --git a/pkg/compute/models/guests.go b/pkg/compute/models/guests.go index 9bac87ea2f..c6094e9200 100644 --- a/pkg/compute/models/guests.go +++ b/pkg/compute/models/guests.go @@ -1073,6 +1073,16 @@ func (guest *SGuest) GetNetworks(netId string) ([]SGuestnetwork, error) { return guestnics, nil } +func (guest *SGuest) GetSlaveNetworks() ([]SGuestnetwork, error) { + guestnics := make([]SGuestnetwork, 0) + q := guest.GetNetworksQuery("").IsNotEmpty("team_with") + err := db.FetchModelObjects(GuestnetworkManager, q, &guestnics) + if err != nil { + return nil, errors.Wrapf(err, "db.FetchModelObjects") + } + return guestnics, nil +} + func (guest *SGuest) ConvertEsxiNetworks(targetGuest *SGuest) error { gns, err := guest.GetNetworks("") if err != nil { diff --git a/pkg/hostman/guestfs/fsdriver/linux.go b/pkg/hostman/guestfs/fsdriver/linux.go index d510404cd7..ebbc310ddc 100644 --- a/pkg/hostman/guestfs/fsdriver/linux.go +++ b/pkg/hostman/guestfs/fsdriver/linux.go @@ -841,7 +841,7 @@ func (d *sDebianLikeRootFs) PrepareFsForTemplate(rootFs IDiskPartition) error { netplanDir := "/etc/netplan/" if rootFs.Exists(netplanDir, false) { for _, f := range rootFs.ListDir(netplanDir, false) { - rootFs.Remove(netplanDir+f, false) + rootFs.Remove(filepath.Join(netplanDir, f), false) } } return nil @@ -885,16 +885,6 @@ func getNicTeamingConfigCmds(slaves []*types.SServerNic) string { 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 @@ -914,24 +904,24 @@ func (d *sDebianLikeRootFs) DeployNetworkingScripts(rootFs IDiskPartition, nics // 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) - log.Debugf("netplanConfig:\n %s", netplanConfig.YAMLString()) - if err := rootFs.FilePutContents(path.Join(netplanDir, "config.yaml"), netplanConfig.YAMLString(), false, false); err != nil { - return errors.Wrap(err, "Put netplan config") - } - } - mainNic := getMainNic(allNics) var mainIp string if mainNic != nil { mainIp = mainNic.Ip } + netplanDir := "/etc/netplan" + if rootFs.Exists(netplanDir, false) { + for _, f := range rootFs.ListDir(netplanDir, false) { + rootFs.Remove(filepath.Join(netplanDir, f), false) + } + netplanConfig := NewNetplanConfig(allNics, bondNics, mainIp) + log.Debugf("netplanConfig:\n %s", netplanConfig.YAMLString()) + if err := rootFs.FilePutContents(path.Join(netplanDir, "config.yaml"), netplanConfig.YAMLString(), false, false); err != nil { + return errors.Wrap(err, "Put netplan config") + } + } + var systemdResolveConfig strings.Builder dnss := []string{} domains := []string{} diff --git a/pkg/hostman/guestfs/fsdriver/netplan.go b/pkg/hostman/guestfs/fsdriver/netplan.go index af3b7e5b56..74b17ecede 100644 --- a/pkg/hostman/guestfs/fsdriver/netplan.go +++ b/pkg/hostman/guestfs/fsdriver/netplan.go @@ -24,16 +24,16 @@ import ( "yunion.io/x/onecloud/pkg/util/netutils2" ) -func NewNetplanConfig(allNics []*types.SServerNic, bondNics []*types.SServerNic) *netplan.Configuration { - network := newNetplanNetwork(allNics, bondNics) +func NewNetplanConfig(allNics []*types.SServerNic, bondNics []*types.SServerNic, mainIp string) *netplan.Configuration { + network := newNetplanNetwork(allNics, bondNics, mainIp) return netplan.NewConfiguration(network) } -func newNetplanNetwork(allNics []*types.SServerNic, bondNics []*types.SServerNic) *netplan.Network { +func newNetplanNetwork(allNics []*types.SServerNic, bondNics []*types.SServerNic, mainIp string) *netplan.Network { network := netplan.NewNetwork() for _, nic := range allNics { - nicConf := getNetplanEthernetConfig(nic, false) + nicConf := getNetplanEthernetConfig(nic, false, mainIp) if nicConf == nil { continue @@ -69,7 +69,7 @@ func newNetplanNetwork(allNics []*types.SServerNic, bondNics []*types.SServerNic network.AddEthernet(sn.Name, nicConf) } - netConf := getNetplanEthernetConfig(bondNic, true) + netConf := getNetplanEthernetConfig(bondNic, true, mainIp) if netConf.Mtu == 0 { netConf.Mtu = defaultMtu @@ -84,7 +84,7 @@ func newNetplanNetwork(allNics []*types.SServerNic, bondNics []*types.SServerNic return network } -func getNetplanEthernetConfig(nic *types.SServerNic, isBond bool) *netplan.EthernetConfig { +func getNetplanEthernetConfig(nic *types.SServerNic, isBond bool, mainIp string) *netplan.EthernetConfig { var nicConf *netplan.EthernetConfig if !isBond && (nic.TeamingMaster != nil || nic.TeamingSlaves != nil) { @@ -94,12 +94,17 @@ func getNetplanEthernetConfig(nic *types.SServerNic, isBond bool) *netplan.Ether nicConf = netplan.NewStaticEthernetConfig(addr, "", "", "", nil, nil, nil) } else if nic.Manual { addr := fmt.Sprintf("%s/%d", nic.Ip, nic.Masklen) - gateway := nic.Gateway + gateway := "" + if nic.Ip == mainIp && len(mainIp) > 0 { + gateway = nic.Gateway + } addr6 := "" gateway6 := "" if len(nic.Ip6) > 0 { addr6 = fmt.Sprintf("%s/%d", nic.Ip6, nic.Masklen6) - gateway6 = nic.Gateway6 + if nic.Ip == mainIp && len(mainIp) > 0 { + gateway6 = nic.Gateway6 + } } var routes []*netplan.Route diff --git a/pkg/hostman/guestfs/fsdriver/netplan_test.go b/pkg/hostman/guestfs/fsdriver/netplan_test.go index fb426f4a7a..b68f5f18e1 100644 --- a/pkg/hostman/guestfs/fsdriver/netplan_test.go +++ b/pkg/hostman/guestfs/fsdriver/netplan_test.go @@ -25,10 +25,12 @@ import ( func TestNewNetplanConfig(t *testing.T) { cases := []struct { - nics []*types.SServerNic - want *netplan.Configuration + mainIp string + nics []*types.SServerNic + want *netplan.Configuration }{ { + mainIp: "10.168.222.175", nics: []*types.SServerNic{ &types.SServerNic{ Name: "eth0", @@ -137,10 +139,119 @@ func TestNewNetplanConfig(t *testing.T) { }, }, }, + { + mainIp: "10.168.22.175", + nics: []*types.SServerNic{ + &types.SServerNic{ + Name: "eth0", + Index: 0, + Bridge: "br0", + Domain: "cloud.local", + Ip: "10.168.222.175", + Vlan: 1, + Driver: "virtio", + Masklen: 24, + Virtual: false, + Manual: true, + WireId: "399a06f3-7925-46c1-8b9f-d5a8580a74df", + NetId: "22c93412-5882-4de0-8357-45ce647ceada", + Mac: "00:24:c7:16:80:f2", + BandWidth: 1000, + Mtu: 1500, + Dns: "8.8.8.8", + Ntp: "", + Net: "vnet222", + Interface: "ens5", + Gateway: "10.168.222.1", + Ifname: "vnet222-175", + Routes: nil, + + LinkUp: true, + }, + &types.SServerNic{ + Name: "eth1", + Index: 1, + Bridge: "br0", + Domain: "cloud.local", + Ip: "", + Vlan: 1, + Driver: "virtio", + Masklen: 0, + Virtual: true, + Manual: true, + WireId: "399a06f3-7925-46c1-8b9f-d5a8580a74df", + NetId: "22c93412-5882-4de0-8357-45ce647ceada", + Mac: "00:24:c7:16:80:f3", + BandWidth: 1000, + Mtu: 1500, + Dns: "8.8.8.8", + Ntp: "", + Net: "vnet222", + Interface: "ens5", + Gateway: "", + Ifname: "vnet222-bpg", + Routes: nil, + + LinkUp: true, + TeamWith: "00:24:c7:16:80:f2", + }, + }, + want: &netplan.Configuration{ + Network: &netplan.Network{ + Version: 2, + Renderer: netplan.NetworkRendererNetworkd, + Ethernets: map[string]*netplan.EthernetConfig{ + "eth0": &netplan.EthernetConfig{ + MacAddress: "00:24:c7:16:80:f2", + Match: &netplan.EthernetConfigMatch{ + MacAddress: "00:24:c7:16:80:f2", + }, + Mtu: 1500, + }, + "eth1": &netplan.EthernetConfig{ + MacAddress: "00:24:c7:16:80:f3", + Match: &netplan.EthernetConfigMatch{ + MacAddress: "00:24:c7:16:80:f3", + }, + Mtu: 1500, + }, + }, + Bonds: map[string]*netplan.Bond{ + "bond0": &netplan.Bond{ + EthernetConfig: netplan.EthernetConfig{ + Addresses: []string{ + "10.168.222.175/24", + }, + MacAddress: "00:24:c7:16:80:f2", + Nameservers: &netplan.Nameservers{ + Search: []string{ + "cloud.local", + }, + Addresses: []string{ + "8.8.8.8", + }, + }, + Mtu: 1500, + }, + Interfaces: []string{ + "eth0", + "eth1", + }, + Parameters: &netplan.BondMode4Params{ + BondModeBaseParams: &netplan.BondModeBaseParams{ + Mode: "802.3ad", + MiiMonitorInterval: 100, + }, + }, + }, + }, + }, + }, + }, } for _, c := range cases { allNics, bondNics := convertNicConfigs(c.nics) - netplanConfig := NewNetplanConfig(allNics, bondNics) + netplanConfig := NewNetplanConfig(allNics, bondNics, c.mainIp) if jsonutils.Marshal(netplanConfig).String() != jsonutils.Marshal(c.want).String() { t.Errorf("nics: %s want: %s got: %s", jsonutils.Marshal(c.nics), jsonutils.Marshal(c.want).PrettyString(), jsonutils.Marshal(netplanConfig).PrettyString()) } diff --git a/pkg/hostman/guestfs/fsdriver/nicteaming.go b/pkg/hostman/guestfs/fsdriver/nicteaming.go index bb60019768..e01ca73994 100644 --- a/pkg/hostman/guestfs/fsdriver/nicteaming.go +++ b/pkg/hostman/guestfs/fsdriver/nicteaming.go @@ -121,6 +121,7 @@ func convertNicConfigs(nics []*types.SServerNic) ([]*types.SServerNic, []*types. nnic.Ip6 = "" nnic.Masklen6 = 0 nnic.Gateway6 = "" + nnic.IsDefault = false tnic.Name = fmt.Sprintf("%s%d", netDevPrefix, tnic.Index) tnic.TeamingMaster = master tnic.Ip = "" @@ -129,6 +130,7 @@ func convertNicConfigs(nics []*types.SServerNic) ([]*types.SServerNic, []*types. tnic.Ip6 = "" tnic.Masklen6 = 0 tnic.Gateway6 = "" + tnic.IsDefault = false master.Name = fmt.Sprintf("bond%d", len(bondNics)) master.TeamingSlaves = []*types.SServerNic{&nnic, &tnic} // why reset master.Mac?