mirror of
https://github.com/yunionio/cloudpods.git
synced 2026-09-01 15:07:17 +08:00
feature: tap service support (#14433)
Co-authored-by: Qiu Jian <qiujian@yunionyun.com>
This commit is contained in:
@@ -0,0 +1,31 @@
|
||||
// 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 compute
|
||||
|
||||
import (
|
||||
"yunion.io/x/onecloud/cmd/climc/shell"
|
||||
modules "yunion.io/x/onecloud/pkg/mcclient/modules/compute"
|
||||
options "yunion.io/x/onecloud/pkg/mcclient/options/compute"
|
||||
)
|
||||
|
||||
func init() {
|
||||
cmd := shell.NewResourceCmd(&modules.NetTapFlows)
|
||||
cmd.Create(&options.NetTapFlowCreateOptions{})
|
||||
cmd.List(&options.NetTapFlowListOptions{})
|
||||
cmd.Show(&options.NetTapFlowIdOptions{})
|
||||
cmd.Delete(&options.NetTapFlowIdOptions{})
|
||||
cmd.Perform("enable", &options.NetTapFlowIdOptions{})
|
||||
cmd.Perform("disable", &options.NetTapFlowIdOptions{})
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
// 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 compute
|
||||
|
||||
import (
|
||||
"yunion.io/x/onecloud/cmd/climc/shell"
|
||||
modules "yunion.io/x/onecloud/pkg/mcclient/modules/compute"
|
||||
options "yunion.io/x/onecloud/pkg/mcclient/options/compute"
|
||||
)
|
||||
|
||||
func init() {
|
||||
cmd := shell.NewResourceCmd(&modules.NetTapServices)
|
||||
cmd.Create(&options.NetTapServiceCreateOptions{})
|
||||
cmd.List(&options.NetTapServiceListOptions{})
|
||||
cmd.Show(&options.NetTapServiceIdOptions{})
|
||||
cmd.Delete(&options.NetTapServiceIdOptions{})
|
||||
cmd.Perform("enable", &options.NetTapServiceIdOptions{})
|
||||
cmd.Perform("disable", &options.NetTapServiceIdOptions{})
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
// 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 compute
|
||||
|
||||
import "yunion.io/x/onecloud/pkg/apis"
|
||||
|
||||
const (
|
||||
TapFlowVSwitch = "vswitch"
|
||||
TapFlowGuestNic = "vnic"
|
||||
|
||||
TapFlowDirectionIn = "IN"
|
||||
TapFlowDirectionOut = "OUT"
|
||||
TapFlowDirectionBoth = "BOTH"
|
||||
|
||||
TapFlowIdMin = 0x10
|
||||
TapFlowIdMax = 0x7fff
|
||||
)
|
||||
|
||||
var (
|
||||
TapFlowDirections = []string{
|
||||
TapFlowDirectionIn,
|
||||
TapFlowDirectionOut,
|
||||
TapFlowDirectionBoth,
|
||||
}
|
||||
)
|
||||
|
||||
type NetTapFlowListInput struct {
|
||||
apis.EnabledStatusStandaloneResourceListInput
|
||||
|
||||
TapId string `json:"tap_id"`
|
||||
}
|
||||
|
||||
type NetTapFlowDetails struct {
|
||||
apis.EnabledStatusStandaloneResourceDetails
|
||||
|
||||
// 关联的tap服务名称
|
||||
Tap string `json:"tap"`
|
||||
|
||||
Source string `json:"source"`
|
||||
|
||||
SourceIps string `json:"source_ips"`
|
||||
|
||||
Net string `json:"net"`
|
||||
}
|
||||
|
||||
type NetTapFlowCreateInput struct {
|
||||
apis.EnabledStatusStandaloneResourceCreateInput
|
||||
|
||||
TapId string `json:"tap_id" required:"true" help:"tap service id or name that this flow belongs to"`
|
||||
|
||||
Type string `json:"type" required:"true" choices:"vswitch|vnic" help:"type of tap flow"`
|
||||
|
||||
HostId string `json:"host_id" help:"id or name of host to tap with"`
|
||||
|
||||
WireId string `json:"wire_id" help:"id or name of wire to tap with"`
|
||||
|
||||
VlanId int `json:"vlan_id" help:"vlan id of vswitch to tap with"`
|
||||
|
||||
GuestId string `json:"guest_id" help:"id or name of vm to tap with"`
|
||||
|
||||
// swagger:ignore
|
||||
NetId string `json:"net_id" ignore:"true"`
|
||||
|
||||
MacAddr string `json:"mac_addr" help:"mac address of guest nic to tap with"`
|
||||
|
||||
IpAddr string `json:"ip_addr" help:"ip address of guest nic to tap with"`
|
||||
|
||||
// swagger:ignore
|
||||
SourceId string `json:"source_id" ignore:"true"`
|
||||
|
||||
Direction string `json:"direction" help:"flow direction" choices:"IN|OUT|BOTH"`
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
// 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 compute
|
||||
|
||||
import "yunion.io/x/onecloud/pkg/apis"
|
||||
|
||||
const (
|
||||
TapServiceHost = "host"
|
||||
TapServiceGuest = "guest"
|
||||
)
|
||||
|
||||
type NetTapServiceListInput struct {
|
||||
apis.EnabledStatusStandaloneResourceListInput
|
||||
}
|
||||
|
||||
type NetTapServiceDetails struct {
|
||||
apis.EnabledStatusStandaloneResourceDetails
|
||||
|
||||
// 流量镜像目标名称
|
||||
Target string `json:"target"`
|
||||
|
||||
// 流量镜像目标IP地址
|
||||
TargetIps string `json:"target_ips"`
|
||||
|
||||
// tap flow数量
|
||||
FlowCount int `json:"flow_count"`
|
||||
}
|
||||
|
||||
type NetTapServiceCreateInput struct {
|
||||
apis.EnabledStatusStandaloneResourceCreateInput
|
||||
|
||||
// TAP服务类型,监听宿主机的网卡还是虚拟机的网卡, 可能值为 host|guest
|
||||
Type string `json:"type" required:"true" choices:"host|guest" help:"type of tap service"`
|
||||
|
||||
// 资源ID,如果Type=host,该值为宿主机的ID,如果Type=guest,该值为虚拟机的ID
|
||||
TargetId string `json:"target_id" required:"true" help:"id of target device"`
|
||||
|
||||
// 监听网卡的Mac地址
|
||||
MacAddr string `json:"mac_addr" help:"mac address of the device interface for tappping"`
|
||||
}
|
||||
@@ -18,7 +18,6 @@ import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"regexp"
|
||||
"time"
|
||||
|
||||
@@ -69,6 +68,7 @@ func init() {
|
||||
}
|
||||
GuestnetworkManager.SetVirtualObject(GuestnetworkManager)
|
||||
GuestnetworkManager.TableSpec().AddIndex(true, "ip_addr", "guest_id")
|
||||
GuestnetworkManager.TableSpec().AddIndex(false, "mac_addr", "deleted")
|
||||
})
|
||||
}
|
||||
|
||||
@@ -78,7 +78,7 @@ type SGuestnetwork struct {
|
||||
NetworkId string `width:"36" charset:"ascii" nullable:"false" list:"user" `
|
||||
|
||||
// MAC地址
|
||||
MacAddr string `width:"32" charset:"ascii" nullable:"false" list:"user"`
|
||||
MacAddr string `width:"32" charset:"ascii" nullable:"false" list:"user" index:"true"`
|
||||
// IPv4地址
|
||||
IpAddr string `width:"16" charset:"ascii" nullable:"false" list:"user"`
|
||||
// IPv6地址
|
||||
@@ -209,37 +209,12 @@ func (manager *SGuestnetworkManager) fetchByRowId(
|
||||
return &gns[0], nil
|
||||
}
|
||||
|
||||
const MAX_TRIES = 10
|
||||
func (manager *SGuestnetworkManager) GenerateMac(suggestion string) (string, error) {
|
||||
return generateMac(suggestion)
|
||||
}
|
||||
|
||||
func (manager *SGuestnetworkManager) GenerateMac(netId string, suggestion string) (string, error) {
|
||||
for tried := 0; tried < MAX_TRIES; tried += 1 {
|
||||
var mac string
|
||||
if len(suggestion) > 0 && regutils.MatchMacAddr(suggestion) {
|
||||
mac = suggestion
|
||||
suggestion = ""
|
||||
} else {
|
||||
b := make([]byte, 4)
|
||||
_, err := rand.Read(b)
|
||||
if err != nil {
|
||||
log.Errorf("generate random mac failed: %s", err)
|
||||
continue
|
||||
}
|
||||
mac = fmt.Sprintf("%s:%02x:%02x:%02x:%02x", options.Options.GlobalMacPrefix, b[0], b[1], b[2], b[3])
|
||||
}
|
||||
q := manager.Query().Equals("mac_addr", mac)
|
||||
if len(netId) > 0 {
|
||||
q = q.Equals("network_id", netId)
|
||||
}
|
||||
cnt, err := q.CountWithError()
|
||||
if err != nil {
|
||||
log.Errorf("find mac %s error %s", mac, err)
|
||||
return "", err
|
||||
}
|
||||
if cnt == 0 {
|
||||
return mac, nil
|
||||
}
|
||||
}
|
||||
return "", fmt.Errorf("maximal retry reached")
|
||||
func (manager *SGuestnetworkManager) FilterByMac(mac string) *sqlchemy.SQuery {
|
||||
return manager.Query().Equals("mac_addr", mac)
|
||||
}
|
||||
|
||||
type newGuestNetworkArgs struct {
|
||||
@@ -313,7 +288,7 @@ func (manager *SGuestnetworkManager) newGuestNetwork(
|
||||
|
||||
provider := vpc.GetProviderName()
|
||||
|
||||
macAddr, err := manager.GenerateMac(network.Id, mac)
|
||||
macAddr, err := manager.GenerateMac(mac)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -115,7 +115,7 @@ func (m *SLoadbalancernetworkManager) NewLoadbalancerNetwork(ctx context.Context
|
||||
lockman.LockObject(ctx, network)
|
||||
defer lockman.ReleaseObject(ctx, network)
|
||||
if req.Loadbalancer.NetworkType == api.LB_NETWORK_TYPE_VPC {
|
||||
macAddr, err := GuestnetworkManager.GenerateMac(network.Id, "")
|
||||
macAddr, err := GuestnetworkManager.GenerateMac("")
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "generate macaddr")
|
||||
}
|
||||
|
||||
@@ -0,0 +1,72 @@
|
||||
// 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 models
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math/rand"
|
||||
|
||||
"yunion.io/x/log"
|
||||
"yunion.io/x/pkg/errors"
|
||||
"yunion.io/x/pkg/util/regutils"
|
||||
"yunion.io/x/sqlchemy"
|
||||
|
||||
"yunion.io/x/onecloud/pkg/compute/options"
|
||||
"yunion.io/x/onecloud/pkg/httperrors"
|
||||
)
|
||||
|
||||
type IMacGenerator interface {
|
||||
FilterByMac(mac string) *sqlchemy.SQuery
|
||||
}
|
||||
|
||||
const maxMacTries = 10
|
||||
|
||||
func generateMac(suggestion string) (string, error) {
|
||||
for tried := 0; tried < maxMacTries; tried += 1 {
|
||||
var mac string
|
||||
if len(suggestion) > 0 && regutils.MatchMacAddr(suggestion) {
|
||||
mac = suggestion
|
||||
suggestion = ""
|
||||
} else {
|
||||
b := make([]byte, 4)
|
||||
_, err := rand.Read(b)
|
||||
if err != nil {
|
||||
log.Errorf("generate random mac failed: %s", err)
|
||||
continue
|
||||
}
|
||||
mac = fmt.Sprintf("%s:%02x:%02x:%02x:%02x", options.Options.GlobalMacPrefix, b[0], b[1], b[2], b[3])
|
||||
}
|
||||
found := false
|
||||
for _, man := range []IMacGenerator{
|
||||
GuestnetworkManager,
|
||||
NetTapServiceManager,
|
||||
} {
|
||||
q := man.FilterByMac(mac)
|
||||
cnt, err := q.CountWithError()
|
||||
if err != nil {
|
||||
log.Errorf("find mac %s error %s", mac, err)
|
||||
return "", err
|
||||
}
|
||||
if cnt > 0 {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
return mac, nil
|
||||
}
|
||||
}
|
||||
return "", errors.Wrap(httperrors.ErrTooManyAttempts, "maximal retry reached")
|
||||
}
|
||||
@@ -144,7 +144,13 @@ func (man *SNatGatewayManager) QueryDistinctExtraField(q *sqlchemy.SQuery, field
|
||||
return q, httperrors.ErrNotFound
|
||||
}
|
||||
|
||||
func (man *SNatGatewayManager) ValidateCreateData(ctx context.Context, userCred mcclient.TokenCredential, ownerId mcclient.IIdentityProvider, query jsonutils.JSONObject, input api.NatgatewayCreateInput) (api.NatgatewayCreateInput, error) {
|
||||
func (man *SNatGatewayManager) ValidateCreateData(
|
||||
ctx context.Context,
|
||||
userCred mcclient.TokenCredential,
|
||||
ownerId mcclient.IIdentityProvider,
|
||||
query jsonutils.JSONObject,
|
||||
input api.NatgatewayCreateInput,
|
||||
) (api.NatgatewayCreateInput, error) {
|
||||
if len(input.NetworkId) == 0 {
|
||||
return input, httperrors.NewMissingParameterError("network_id")
|
||||
}
|
||||
@@ -216,7 +222,13 @@ func (man *SNatGatewayManager) ValidateCreateData(ctx context.Context, userCred
|
||||
return driver.ValidateCreateNatGateway(ctx, userCred, input)
|
||||
}
|
||||
|
||||
func (self *SNatGateway) PostCreate(ctx context.Context, userCred mcclient.TokenCredential, ownerId mcclient.IIdentityProvider, query jsonutils.JSONObject, data jsonutils.JSONObject) {
|
||||
func (self *SNatGateway) PostCreate(
|
||||
ctx context.Context,
|
||||
userCred mcclient.TokenCredential,
|
||||
ownerId mcclient.IIdentityProvider,
|
||||
query jsonutils.JSONObject,
|
||||
data jsonutils.JSONObject,
|
||||
) {
|
||||
self.SInfrasResourceBase.PostCreate(ctx, userCred, ownerId, query, data)
|
||||
|
||||
err := self.StartNatGatewayCreateTask(ctx, userCred, data.(*jsonutils.JSONDict))
|
||||
@@ -348,7 +360,7 @@ func (self *SNatGateway) GetDTableSize(filter func(q *sqlchemy.SQuery) *sqlchemy
|
||||
return q.CountWithError()
|
||||
}
|
||||
|
||||
func (manager SNatGatewayManager) FetchCustomizeColumns(
|
||||
func (manager *SNatGatewayManager) FetchCustomizeColumns(
|
||||
ctx context.Context,
|
||||
userCred mcclient.TokenCredential,
|
||||
query jsonutils.JSONObject,
|
||||
|
||||
@@ -0,0 +1,337 @@
|
||||
// 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 models
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"strings"
|
||||
|
||||
"yunion.io/x/jsonutils"
|
||||
"yunion.io/x/log"
|
||||
"yunion.io/x/pkg/errors"
|
||||
"yunion.io/x/pkg/tristate"
|
||||
"yunion.io/x/pkg/utils"
|
||||
"yunion.io/x/sqlchemy"
|
||||
|
||||
api "yunion.io/x/onecloud/pkg/apis/compute"
|
||||
"yunion.io/x/onecloud/pkg/cloudcommon/db"
|
||||
"yunion.io/x/onecloud/pkg/cloudcommon/db/lockman"
|
||||
"yunion.io/x/onecloud/pkg/httperrors"
|
||||
"yunion.io/x/onecloud/pkg/mcclient"
|
||||
"yunion.io/x/onecloud/pkg/util/stringutils2"
|
||||
)
|
||||
|
||||
type SNetTapFlowManager struct {
|
||||
db.SEnabledStatusStandaloneResourceBaseManager
|
||||
}
|
||||
|
||||
var NetTapFlowManager *SNetTapFlowManager
|
||||
|
||||
func init() {
|
||||
NetTapFlowManager = &SNetTapFlowManager{
|
||||
SEnabledStatusStandaloneResourceBaseManager: db.NewEnabledStatusStandaloneResourceBaseManager(
|
||||
SNetTapFlow{},
|
||||
"net_tap_flows_tbl",
|
||||
"tap_flow",
|
||||
"tap_flows",
|
||||
),
|
||||
}
|
||||
NetTapFlowManager.SetVirtualObject(NetTapFlowManager)
|
||||
}
|
||||
|
||||
type SNetTapFlow struct {
|
||||
db.SEnabledStatusStandaloneResourceBase
|
||||
|
||||
TapId string `width:"36" charset:"ascii" nullable:"false" list:"admin" create:"admin_required"`
|
||||
|
||||
Type string `width:"10" charset:"ascii" list:"admin" create:"admin_required"`
|
||||
|
||||
SourceId string `width:"36" charset:"ascii" nullable:"false" list:"admin" create:"admin_required"`
|
||||
|
||||
NetId string `width:"36" charset:"ascii" nullable:"false" list:"admin" create:"admin_required"`
|
||||
|
||||
MacAddr string `width:"18" charset:"ascii" list:"admin" create:"admin_optional"`
|
||||
|
||||
VlanId int `nullable:"true" list:"admin" create:"admin_optional"`
|
||||
|
||||
Direction string `width:"6" charset:"ascii" list:"admin" create:"admin_required" default:"BOTH"`
|
||||
|
||||
FlowId uint16 `nullable:"false" list:"admin"`
|
||||
}
|
||||
|
||||
func (man *SNetTapFlowManager) ListItemFilter(
|
||||
ctx context.Context,
|
||||
q *sqlchemy.SQuery,
|
||||
userCred mcclient.TokenCredential,
|
||||
query api.NetTapFlowListInput,
|
||||
) (*sqlchemy.SQuery, error) {
|
||||
q, err := man.SEnabledStatusStandaloneResourceBaseManager.ListItemFilter(ctx, q, userCred, query.EnabledStatusStandaloneResourceListInput)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "SEnabledStatusInfrasResourceBaseManager.ListItemFilter")
|
||||
}
|
||||
if len(query.TapId) > 0 {
|
||||
tapObj, err := NetTapServiceManager.FetchByIdOrName(userCred, query.TapId)
|
||||
if err != nil {
|
||||
if errors.Cause(err) == sql.ErrNoRows {
|
||||
return nil, errors.Wrapf(httperrors.ErrResourceNotFound, "%s %s not found", NetTapServiceManager.Keyword(), query.TapId)
|
||||
} else {
|
||||
return nil, errors.Wrap(err, "NetTapServiceManager.FetchByIdOrName")
|
||||
}
|
||||
}
|
||||
q = q.Equals("tap_id", tapObj.GetId())
|
||||
}
|
||||
return q, nil
|
||||
}
|
||||
|
||||
func (man *SNetTapFlowManager) OrderByExtraFields(
|
||||
ctx context.Context,
|
||||
q *sqlchemy.SQuery,
|
||||
userCred mcclient.TokenCredential,
|
||||
query api.NetTapFlowListInput,
|
||||
) (*sqlchemy.SQuery, error) {
|
||||
q, err := man.SEnabledStatusStandaloneResourceBaseManager.OrderByExtraFields(ctx, q, userCred, query.EnabledStatusStandaloneResourceListInput)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "SEnabledStatusInfrasResourceBaseManager.OrderByExtraFields")
|
||||
}
|
||||
return q, nil
|
||||
}
|
||||
|
||||
func (man *SNetTapFlowManager) QueryDistinctExtraField(q *sqlchemy.SQuery, field string) (*sqlchemy.SQuery, error) {
|
||||
var err error
|
||||
q, err = man.SEnabledStatusStandaloneResourceBaseManager.QueryDistinctExtraField(q, field)
|
||||
if err == nil {
|
||||
return q, nil
|
||||
}
|
||||
|
||||
return q, httperrors.ErrNotFound
|
||||
}
|
||||
|
||||
func (manager *SNetTapFlowManager) FetchCustomizeColumns(
|
||||
ctx context.Context,
|
||||
userCred mcclient.TokenCredential,
|
||||
query jsonutils.JSONObject,
|
||||
objs []interface{},
|
||||
fields stringutils2.SSortedStrings,
|
||||
isList bool,
|
||||
) []api.NetTapFlowDetails {
|
||||
rows := make([]api.NetTapFlowDetails, len(objs))
|
||||
stdRows := manager.SEnabledStatusStandaloneResourceBaseManager.FetchCustomizeColumns(ctx, userCred, query, objs, fields, isList)
|
||||
tapIds := make([]string, len(objs))
|
||||
for i := range rows {
|
||||
rows[i] = api.NetTapFlowDetails{
|
||||
EnabledStatusStandaloneResourceDetails: stdRows[i],
|
||||
}
|
||||
flow := objs[i].(*SNetTapFlow)
|
||||
tapIds[i] = flow.TapId
|
||||
}
|
||||
tapIdMap, err := db.FetchIdNameMap2(NetTapServiceManager, tapIds)
|
||||
if err != nil {
|
||||
log.Errorf("FetchIdNameMap2 fail: %s", err)
|
||||
return rows
|
||||
}
|
||||
for i := range rows {
|
||||
if name, ok := tapIdMap[tapIds[i]]; ok {
|
||||
rows[i].Tap = name
|
||||
}
|
||||
}
|
||||
return rows
|
||||
}
|
||||
|
||||
func (flow *SNetTapFlow) getMoreDetails(ctx context.Context, details api.NetTapFlowDetails) api.NetTapFlowDetails {
|
||||
switch flow.Type {
|
||||
case api.TapFlowVSwitch:
|
||||
host := HostManager.FetchHostById(flow.SourceId)
|
||||
details.Source = host.Name
|
||||
details.SourceIps = host.AccessIp
|
||||
wire := WireManager.FetchWireById(flow.NetId)
|
||||
details.Net = wire.Name
|
||||
case api.TapFlowGuestNic:
|
||||
guest := GuestManager.FetchGuestById(flow.SourceId)
|
||||
details.Source = guest.Name
|
||||
ret := fetchGuestIPs([]string{flow.SourceId}, tristate.False)
|
||||
details.SourceIps = strings.Join(ret[flow.SourceId], ",")
|
||||
netObj, _ := NetworkManager.FetchById(flow.NetId)
|
||||
details.Net = netObj.GetName()
|
||||
}
|
||||
return details
|
||||
}
|
||||
|
||||
func (manager *SNetTapFlowManager) ValidateCreateData(
|
||||
ctx context.Context,
|
||||
userCred mcclient.TokenCredential,
|
||||
ownerId mcclient.IIdentityProvider,
|
||||
query jsonutils.JSONObject,
|
||||
input api.NetTapFlowCreateInput,
|
||||
) (api.NetTapFlowCreateInput, error) {
|
||||
var err error
|
||||
input.EnabledStatusStandaloneResourceCreateInput, err = manager.SEnabledStatusStandaloneResourceBaseManager.ValidateCreateData(ctx, userCred, ownerId, query, input.EnabledStatusStandaloneResourceCreateInput)
|
||||
if err != nil {
|
||||
return input, errors.Wrap(err, "SEnabledStatusInfrasResourceBaseManager.ValidateCreateData(")
|
||||
}
|
||||
tapObj, err := NetTapServiceManager.FetchByIdOrName(userCred, input.TapId)
|
||||
if err != nil {
|
||||
if errors.Cause(err) == sql.ErrNoRows {
|
||||
return input, httperrors.NewResourceNotFoundError2(NetTapServiceManager.Keyword(), input.TapId)
|
||||
} else {
|
||||
return input, errors.Wrap(err, "NetTapServiceManager.FetchByIdOrName")
|
||||
}
|
||||
}
|
||||
input.TapId = tapObj.GetId()
|
||||
switch input.Type {
|
||||
case api.TapFlowVSwitch:
|
||||
hostObj, err := HostManager.FetchByIdOrName(userCred, input.HostId)
|
||||
if err != nil {
|
||||
if errors.Cause(err) == sql.ErrNoRows {
|
||||
return input, httperrors.NewResourceNotFoundError2(HostManager.Keyword(), input.HostId)
|
||||
} else {
|
||||
return input, errors.Wrap(err, "HostManager.FetchByIdOrName")
|
||||
}
|
||||
}
|
||||
wireObj, err := WireManager.FetchByIdOrName(userCred, input.WireId)
|
||||
if err != nil {
|
||||
if errors.Cause(err) == sql.ErrNoRows {
|
||||
return input, httperrors.NewResourceNotFoundError2(WireManager.Keyword(), input.WireId)
|
||||
} else {
|
||||
return input, errors.Wrap(err, "WireManager.FetchByIdOrName")
|
||||
}
|
||||
}
|
||||
host := hostObj.(*SHost)
|
||||
if host.HostType != api.HOST_TYPE_HYPERVISOR {
|
||||
return input, errors.Wrapf(httperrors.ErrNotSupported, "host type %s not supported", host.HostType)
|
||||
}
|
||||
wire := wireObj.(*SWire)
|
||||
netifs := host.GetNetifsOnWire(wire)
|
||||
if len(netifs) == 0 {
|
||||
return input, errors.Wrapf(httperrors.ErrInvalidStatus, "host %s and wire %s not attached", input.HostId, input.WireId)
|
||||
}
|
||||
ipmiCnt := 0
|
||||
nicCnt := 0
|
||||
for _, netif := range netifs {
|
||||
if netif.NicType == api.NIC_TYPE_IPMI {
|
||||
ipmiCnt++
|
||||
}
|
||||
nicCnt++
|
||||
}
|
||||
if ipmiCnt == nicCnt {
|
||||
return input, errors.Wrapf(httperrors.ErrInvalidStatus, "host %s and wire %s attached with IPMI links", input.HostId, input.WireId)
|
||||
}
|
||||
input.SourceId = host.Id
|
||||
input.MacAddr = ""
|
||||
input.NetId = wire.Id
|
||||
if input.VlanId <= 0 || input.VlanId > 4095 {
|
||||
return input, errors.Wrapf(httperrors.ErrInputParameter, "invalid vlan id %d", input.VlanId)
|
||||
}
|
||||
case api.TapFlowGuestNic:
|
||||
guestObj, err := GuestManager.FetchByIdOrName(userCred, input.GuestId)
|
||||
if err != nil {
|
||||
if errors.Cause(err) == sql.ErrNoRows {
|
||||
return input, httperrors.NewResourceNotFoundError2(GuestManager.Keyword(), input.GuestId)
|
||||
} else {
|
||||
return input, errors.Wrap(err, "GuestManager.FetchByIdOrName")
|
||||
}
|
||||
}
|
||||
guest := guestObj.(*SGuest)
|
||||
if guest.Hypervisor != api.HYPERVISOR_KVM {
|
||||
return input, errors.Wrapf(httperrors.ErrInvalidStatus, "hypervisor %s not supported", guest.Hypervisor)
|
||||
}
|
||||
gns, err := GuestnetworkManager.FetchByGuestId(guest.Id)
|
||||
if err != nil {
|
||||
return input, errors.Wrap(err, "GuestnetworkManager.FetchByGuestId")
|
||||
}
|
||||
var gn *SGuestnetwork
|
||||
if len(input.IpAddr) == 0 && len(input.MacAddr) == 0 {
|
||||
if len(gns) == 1 {
|
||||
gn = &gns[0]
|
||||
} else {
|
||||
return input, errors.Wrap(httperrors.ErrInputParameter, "either ip_addr or mac_addr should be specified")
|
||||
}
|
||||
} else {
|
||||
for i := range gns {
|
||||
if (len(input.IpAddr) > 0 && input.IpAddr == gns[i].IpAddr) || (len(input.MacAddr) > 0 && input.MacAddr == gns[i].MacAddr) {
|
||||
gn = &gns[i]
|
||||
break
|
||||
}
|
||||
}
|
||||
if gn == nil {
|
||||
return input, errors.Wrap(httperrors.ErrNotFound, "Guest network not found")
|
||||
}
|
||||
}
|
||||
input.SourceId = guest.Id
|
||||
input.MacAddr = gn.MacAddr
|
||||
input.NetId = gn.NetworkId
|
||||
input.VlanId = 0
|
||||
default:
|
||||
return input, errors.Wrapf(httperrors.ErrInputParameter, "invalid flow type %s", input.Type)
|
||||
}
|
||||
if len(input.Direction) == 0 {
|
||||
input.Direction = api.TapFlowDirectionBoth
|
||||
}
|
||||
if !utils.IsInStringArray(input.Direction, api.TapFlowDirections) {
|
||||
return input, errors.Wrapf(httperrors.ErrNotSupported, "unsupported direction %s", input.Direction)
|
||||
}
|
||||
return input, nil
|
||||
}
|
||||
|
||||
func (tap *SNetTapFlow) CustomizeCreate(
|
||||
ctx context.Context,
|
||||
userCred mcclient.TokenCredential,
|
||||
ownerId mcclient.IIdentityProvider,
|
||||
query jsonutils.JSONObject,
|
||||
data jsonutils.JSONObject,
|
||||
) error {
|
||||
// generate flowId
|
||||
err := func() error {
|
||||
lockman.LockClass(ctx, NetTapFlowManager, "")
|
||||
defer lockman.ReleaseClass(ctx, NetTapFlowManager, "")
|
||||
|
||||
flowId, err := NetTapFlowManager.getFreeFlowId()
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "getFreeFlowId")
|
||||
}
|
||||
tap.FlowId = flowId
|
||||
return nil
|
||||
}()
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "generate flow id")
|
||||
}
|
||||
return tap.SEnabledStatusStandaloneResourceBase.CustomizeCreate(ctx, userCred, ownerId, query, data)
|
||||
}
|
||||
|
||||
func (manager *SNetTapFlowManager) getFreeFlowId() (uint16, error) {
|
||||
flowIds := make([]struct {
|
||||
FlowId uint16 `json:"flow_id"`
|
||||
}, 0)
|
||||
q := manager.Query("flow_id").Asc("flow_id")
|
||||
err := q.All(&flowIds)
|
||||
if err != nil && errors.Cause(err) != sql.ErrNoRows {
|
||||
return 0, errors.Wrap(err, "queryAll")
|
||||
}
|
||||
if len(flowIds) == 0 {
|
||||
return api.TapFlowIdMin, nil
|
||||
}
|
||||
if flowIds[0].FlowId > api.TapFlowIdMin {
|
||||
return flowIds[0].FlowId - 1, nil
|
||||
}
|
||||
if flowIds[len(flowIds)-1].FlowId < api.TapFlowIdMax {
|
||||
return flowIds[len(flowIds)-1].FlowId + 1, nil
|
||||
}
|
||||
for i := 0; i < len(flowIds)-1; i++ {
|
||||
if flowIds[i].FlowId+1 < flowIds[i+1].FlowId {
|
||||
return flowIds[i].FlowId + 1, nil
|
||||
}
|
||||
}
|
||||
return 0, errors.Wrap(httperrors.ErrOutOfResource, "run out of flow id!!!")
|
||||
}
|
||||
@@ -0,0 +1,325 @@
|
||||
// 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 models
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"strings"
|
||||
|
||||
"yunion.io/x/jsonutils"
|
||||
"yunion.io/x/log"
|
||||
"yunion.io/x/pkg/errors"
|
||||
"yunion.io/x/pkg/tristate"
|
||||
"yunion.io/x/pkg/util/netutils"
|
||||
"yunion.io/x/sqlchemy"
|
||||
|
||||
api "yunion.io/x/onecloud/pkg/apis/compute"
|
||||
"yunion.io/x/onecloud/pkg/cloudcommon/db"
|
||||
"yunion.io/x/onecloud/pkg/cloudcommon/db/lockman"
|
||||
"yunion.io/x/onecloud/pkg/httperrors"
|
||||
"yunion.io/x/onecloud/pkg/mcclient"
|
||||
"yunion.io/x/onecloud/pkg/util/seclib2"
|
||||
"yunion.io/x/onecloud/pkg/util/stringutils2"
|
||||
)
|
||||
|
||||
type SNetTapServiceManager struct {
|
||||
db.SEnabledStatusStandaloneResourceBaseManager
|
||||
}
|
||||
|
||||
var NetTapServiceManager *SNetTapServiceManager
|
||||
|
||||
func init() {
|
||||
NetTapServiceManager = &SNetTapServiceManager{
|
||||
SEnabledStatusStandaloneResourceBaseManager: db.NewEnabledStatusStandaloneResourceBaseManager(
|
||||
SNetTapService{},
|
||||
"net_tap_services_tbl",
|
||||
"tap_service",
|
||||
"tap_services",
|
||||
),
|
||||
}
|
||||
NetTapServiceManager.SetVirtualObject(NetTapServiceManager)
|
||||
NetTapServiceManager.TableSpec().AddIndex(false, "mac_addr", "deleted")
|
||||
}
|
||||
|
||||
type SNetTapService struct {
|
||||
db.SEnabledStatusStandaloneResourceBase
|
||||
|
||||
// 流量采集端类型,虚拟机(guest)还是宿主机(host)
|
||||
Type string `width:"10" charset:"ascii" list:"admin" create:"admin_required"`
|
||||
// 接受流量的目标ID,如果type=host,是hostId,如果type=guest,是guestId
|
||||
TargetId string `width:"36" charset:"ascii" nullable:"false" list:"admin" create:"admin_required"`
|
||||
// 接受流量的Mac地址
|
||||
MacAddr string `width:"18" charset:"ascii" list:"admin" create:"admin_optional"`
|
||||
// 网卡名称
|
||||
Ifname string `width:"16" charset:"ascii" nullable:"true" list:"admin"`
|
||||
}
|
||||
|
||||
func (man *SNetTapServiceManager) ListItemFilter(
|
||||
ctx context.Context,
|
||||
q *sqlchemy.SQuery,
|
||||
userCred mcclient.TokenCredential,
|
||||
query api.NetTapServiceListInput,
|
||||
) (*sqlchemy.SQuery, error) {
|
||||
q, err := man.SEnabledStatusStandaloneResourceBaseManager.ListItemFilter(ctx, q, userCred, query.EnabledStatusStandaloneResourceListInput)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "SEnabledStatusStandaloneResourceBaseManager.ListItemFilter")
|
||||
}
|
||||
return q, nil
|
||||
}
|
||||
|
||||
func (man *SNetTapServiceManager) OrderByExtraFields(
|
||||
ctx context.Context,
|
||||
q *sqlchemy.SQuery,
|
||||
userCred mcclient.TokenCredential,
|
||||
query api.NetTapServiceListInput,
|
||||
) (*sqlchemy.SQuery, error) {
|
||||
q, err := man.SEnabledStatusStandaloneResourceBaseManager.OrderByExtraFields(ctx, q, userCred, query.EnabledStatusStandaloneResourceListInput)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "SEnabledStatusInfrasResourceBaseManager.OrderByExtraFields")
|
||||
}
|
||||
return q, nil
|
||||
}
|
||||
|
||||
func (man *SNetTapServiceManager) QueryDistinctExtraField(q *sqlchemy.SQuery, field string) (*sqlchemy.SQuery, error) {
|
||||
var err error
|
||||
q, err = man.SEnabledStatusStandaloneResourceBaseManager.QueryDistinctExtraField(q, field)
|
||||
if err == nil {
|
||||
return q, nil
|
||||
}
|
||||
|
||||
return q, httperrors.ErrNotFound
|
||||
}
|
||||
|
||||
func (manager *SNetTapServiceManager) FetchCustomizeColumns(
|
||||
ctx context.Context,
|
||||
userCred mcclient.TokenCredential,
|
||||
query jsonutils.JSONObject,
|
||||
objs []interface{},
|
||||
fields stringutils2.SSortedStrings,
|
||||
isList bool,
|
||||
) []api.NetTapServiceDetails {
|
||||
rows := make([]api.NetTapServiceDetails, len(objs))
|
||||
stdRows := manager.SEnabledStatusStandaloneResourceBaseManager.FetchCustomizeColumns(ctx, userCred, query, objs, fields, isList)
|
||||
for i := range rows {
|
||||
rows[i] = api.NetTapServiceDetails{
|
||||
EnabledStatusStandaloneResourceDetails: stdRows[i],
|
||||
}
|
||||
rows[i] = objs[i].(*SNetTapService).getMoreDetails(ctx, rows[i])
|
||||
}
|
||||
return rows
|
||||
}
|
||||
|
||||
func (srv *SNetTapService) getMoreDetails(ctx context.Context, details api.NetTapServiceDetails) api.NetTapServiceDetails {
|
||||
var err error
|
||||
switch srv.Type {
|
||||
case api.TapServiceHost:
|
||||
host := HostManager.FetchHostById(srv.TargetId)
|
||||
details.Target = host.Name
|
||||
details.TargetIps = host.AccessIp
|
||||
case api.TapServiceGuest:
|
||||
guest := GuestManager.FetchGuestById(srv.TargetId)
|
||||
details.Target = guest.Name
|
||||
ret := fetchGuestIPs([]string{srv.TargetId}, tristate.False)
|
||||
details.TargetIps = strings.Join(ret[srv.TargetId], ",")
|
||||
}
|
||||
details.FlowCount, err = srv.getFlowsCount()
|
||||
if err != nil {
|
||||
log.Errorf("getFlowsCount %s", err)
|
||||
}
|
||||
return details
|
||||
}
|
||||
|
||||
func (srv *SNetTapService) getFlowsQuery() *sqlchemy.SQuery {
|
||||
return NetTapFlowManager.Query().Equals("tap_id", srv.Id)
|
||||
}
|
||||
|
||||
func (srv *SNetTapService) getFlowsCount() (int, error) {
|
||||
return srv.getFlowsQuery().CountWithError()
|
||||
}
|
||||
|
||||
func (srv *SNetTapService) getFlows() ([]SNetTapFlow, error) {
|
||||
flows := make([]SNetTapFlow, 0)
|
||||
q := srv.getFlowsQuery()
|
||||
err := db.FetchModelObjects(NetTapFlowManager, q, &flows)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "FetchModelObjects")
|
||||
}
|
||||
return flows, nil
|
||||
}
|
||||
|
||||
func (srv *SNetTapService) ValidateDeleteCondition(ctx context.Context, info jsonutils.JSONObject) error {
|
||||
cnt, err := srv.getFlowsCount()
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "getFlowCount")
|
||||
}
|
||||
if cnt > 0 {
|
||||
return httperrors.NewNotEmptyError("Tap service has associated flows")
|
||||
}
|
||||
return srv.SEnabledStatusStandaloneResourceBase.ValidateDeleteCondition(ctx, info)
|
||||
}
|
||||
|
||||
func (manager *SNetTapServiceManager) ValidateCreateData(
|
||||
ctx context.Context,
|
||||
userCred mcclient.TokenCredential,
|
||||
ownerId mcclient.IIdentityProvider,
|
||||
query jsonutils.JSONObject,
|
||||
input api.NetTapServiceCreateInput,
|
||||
) (api.NetTapServiceCreateInput, error) {
|
||||
var err error
|
||||
input.EnabledStatusStandaloneResourceCreateInput, err = manager.SEnabledStatusStandaloneResourceBaseManager.ValidateCreateData(ctx, userCred, ownerId, query, input.EnabledStatusStandaloneResourceCreateInput)
|
||||
if err != nil {
|
||||
return input, errors.Wrap(err, "SEnabledStatusInfrasResourceBaseManager.ValidateCreateData(")
|
||||
}
|
||||
switch input.Type {
|
||||
case api.TapServiceHost:
|
||||
hostObj, err := HostManager.FetchByIdOrName(userCred, input.TargetId)
|
||||
if err != nil {
|
||||
if errors.Cause(err) == sql.ErrNoRows {
|
||||
return input, errors.Wrapf(httperrors.ErrResourceNotFound, "%s %s", HostManager.Keyword(), input.TargetId)
|
||||
} else {
|
||||
return input, errors.Wrap(err, "HostManager.FetchByIdOrName")
|
||||
}
|
||||
}
|
||||
host := hostObj.(*SHost)
|
||||
if host.HostType != api.HOST_TYPE_HYPERVISOR {
|
||||
return input, errors.Wrapf(httperrors.ErrNotSupported, "host type %s not supported", host.HostType)
|
||||
}
|
||||
if len(input.MacAddr) > 0 {
|
||||
input.MacAddr = netutils.FormatMacAddr(input.MacAddr)
|
||||
nic := host.GetNetInterface(input.MacAddr)
|
||||
if nic == nil {
|
||||
return input, errors.Wrap(errors.ErrNotFound, "host.GetNetInterface")
|
||||
}
|
||||
if len(nic.WireId) > 0 {
|
||||
return input, errors.Wrap(httperrors.ErrNotEmpty, "interface has been used")
|
||||
}
|
||||
}
|
||||
_, err = manager.fetchByHostIdMac(hostObj.GetId(), input.MacAddr)
|
||||
if err == nil {
|
||||
return input, errors.Wrapf(httperrors.ErrNotEmpty, "host %s(%s) has been attached to tap service", input.TargetId, input.MacAddr)
|
||||
} else if errors.Cause(err) != sql.ErrNoRows {
|
||||
return input, errors.Wrap(err, "fetchGuestById")
|
||||
}
|
||||
input.TargetId = hostObj.GetId()
|
||||
case api.TapServiceGuest:
|
||||
guestObj, err := GuestManager.FetchByIdOrName(userCred, input.TargetId)
|
||||
if err != nil {
|
||||
if errors.Cause(err) == sql.ErrNoRows {
|
||||
return input, errors.Wrapf(httperrors.ErrResourceNotFound, "%s %s", GuestManager.Keyword(), input.TargetId)
|
||||
} else {
|
||||
return input, errors.Wrap(err, "GuestManager.FetchByIdOrName")
|
||||
}
|
||||
}
|
||||
guest := guestObj.(*SGuest)
|
||||
if guest.Hypervisor != api.HYPERVISOR_KVM {
|
||||
return input, errors.Wrapf(httperrors.ErrNotSupported, "hypervisor %s not supported", guest.Hypervisor)
|
||||
}
|
||||
// check the guest attach to tap
|
||||
_, err = manager.fetchByGuestId(guestObj.GetId())
|
||||
if err == nil {
|
||||
return input, errors.Wrapf(httperrors.ErrNotEmpty, "guest %s has been attached to tap service", input.TargetId)
|
||||
} else if errors.Cause(err) != sql.ErrNoRows {
|
||||
return input, errors.Wrap(err, "fetchGuestById")
|
||||
}
|
||||
input.TargetId = guestObj.GetId()
|
||||
default:
|
||||
return input, errors.Wrapf(httperrors.ErrNotSupported, "unsupported type %s", input.Type)
|
||||
}
|
||||
return input, nil
|
||||
}
|
||||
|
||||
func (man *SNetTapServiceManager) fetchByGuestId(guestId string) (*SNetTapService, error) {
|
||||
return man.fetchByTargetMac(api.TapServiceGuest, guestId, "")
|
||||
}
|
||||
|
||||
func (man *SNetTapServiceManager) fetchByHostIdMac(hostId, mac string) (*SNetTapService, error) {
|
||||
return man.fetchByTargetMac(api.TapServiceHost, hostId, mac)
|
||||
}
|
||||
|
||||
func (man *SNetTapServiceManager) fetchByTargetMac(tapTyep, targetId, mac string) (*SNetTapService, error) {
|
||||
q := man.Query().Equals("type", api.TapServiceGuest).Equals("target_id", targetId)
|
||||
if len(mac) > 0 {
|
||||
q = q.Equals("mac_addr", mac)
|
||||
}
|
||||
tapObj, err := db.NewModelObject(man)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "NewModelObject")
|
||||
}
|
||||
err = q.First(tapObj)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "First")
|
||||
}
|
||||
return tapObj.(*SNetTapService), nil
|
||||
}
|
||||
|
||||
func (tap *SNetTapService) CustomizeCreate(
|
||||
ctx context.Context,
|
||||
userCred mcclient.TokenCredential,
|
||||
ownerId mcclient.IIdentityProvider,
|
||||
query jsonutils.JSONObject,
|
||||
data jsonutils.JSONObject,
|
||||
) error {
|
||||
input := api.NetTapServiceCreateInput{}
|
||||
err := data.Unmarshal(&input)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "Unmarshal NetTapServiceCreateInput")
|
||||
}
|
||||
switch input.Type {
|
||||
case api.TapServiceGuest:
|
||||
err := func() error {
|
||||
lockman.LockClass(ctx, NetTapServiceManager, "")
|
||||
defer lockman.ReleaseClass(ctx, NetTapServiceManager, "")
|
||||
// generate mac
|
||||
mac, err := NetTapServiceManager.GenerateMac(input.MacAddr)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "GenerateMac")
|
||||
}
|
||||
ifname, err := NetTapServiceManager.generateIfname(mac)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "generateIfname")
|
||||
}
|
||||
tap.Ifname = ifname
|
||||
tap.MacAddr = mac
|
||||
return nil
|
||||
}()
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "generate mac and ifname")
|
||||
}
|
||||
}
|
||||
return tap.SEnabledStatusStandaloneResourceBase.CustomizeCreate(ctx, userCred, ownerId, query, data)
|
||||
}
|
||||
|
||||
func (manager *SNetTapServiceManager) generateIfname(seed string) (string, error) {
|
||||
for tried := 0; tried < maxMacTries; tried++ {
|
||||
ifname := "tap" + seclib2.HashId(seed, byte(tried), 6)
|
||||
cnt, err := manager.Query().Equals("ifname", ifname).CountWithError()
|
||||
if err != nil {
|
||||
return "", errors.Wrap(err, "CountWithError")
|
||||
}
|
||||
if cnt == 0 {
|
||||
return ifname, nil
|
||||
}
|
||||
}
|
||||
return "", errors.Wrap(httperrors.ErrTooManyAttempts, "maximal retry reached")
|
||||
}
|
||||
|
||||
func (manager *SNetTapServiceManager) GenerateMac(suggestion string) (string, error) {
|
||||
return generateMac(suggestion)
|
||||
}
|
||||
|
||||
func (manager *SNetTapServiceManager) FilterByMac(mac string) *sqlchemy.SQuery {
|
||||
return manager.Query().Equals("mac_addr", mac)
|
||||
}
|
||||
@@ -243,6 +243,9 @@ func InitHandlers(app *appsrv.Application) {
|
||||
models.InstanceBackupManager,
|
||||
|
||||
models.IPv6GatewayManager,
|
||||
|
||||
models.NetTapServiceManager,
|
||||
models.NetTapFlowManager,
|
||||
} {
|
||||
db.RegisterModelManager(manager)
|
||||
handler := db.NewModelHandler(manager)
|
||||
|
||||
@@ -40,6 +40,7 @@ import (
|
||||
identityapi "yunion.io/x/onecloud/pkg/apis/identity"
|
||||
napi "yunion.io/x/onecloud/pkg/apis/notify"
|
||||
"yunion.io/x/onecloud/pkg/cloudcommon/notifyclient"
|
||||
"yunion.io/x/onecloud/pkg/cloudcommon/types"
|
||||
"yunion.io/x/onecloud/pkg/hostman/guestfs/fsdriver"
|
||||
"yunion.io/x/onecloud/pkg/hostman/host_health"
|
||||
deployapi "yunion.io/x/onecloud/pkg/hostman/hostdeployer/apis"
|
||||
@@ -1337,6 +1338,18 @@ func (h *SHostInfo) getNetworkInfo() {
|
||||
}
|
||||
|
||||
func (h *SHostInfo) uploadNetworkInfo() {
|
||||
phyNics, err := sysutils.Nics()
|
||||
if err != nil {
|
||||
h.onFail(errors.Wrap(err, "parse physical nics info"))
|
||||
return
|
||||
}
|
||||
for _, pnic := range phyNics {
|
||||
err := h.doSendPhysicalNicInfo(pnic)
|
||||
if err != nil {
|
||||
h.onFail(errors.Wrapf(err, "doSendPhysicalNicInfo %s", pnic.Dev))
|
||||
return
|
||||
}
|
||||
}
|
||||
for _, nic := range h.Nics {
|
||||
if len(nic.WireId) == 0 {
|
||||
if len(nic.Network) == 0 {
|
||||
@@ -1352,30 +1365,57 @@ func (h *SHostInfo) uploadNetworkInfo() {
|
||||
return
|
||||
} else {
|
||||
nic.Network, _ = wireInfo.GetString("name")
|
||||
h.doUploadNicInfo(nic)
|
||||
err := h.doUploadNicInfo(nic)
|
||||
if err != nil {
|
||||
h.onFail(errors.Wrapf(err, "doUploadNicInfo %s", nic.Inter))
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
h.doUploadNicInfo(nic)
|
||||
err := h.doUploadNicInfo(nic)
|
||||
if err != nil {
|
||||
h.onFail(errors.Wrapf(err, "doUploadNicInfo %s", nic.Inter))
|
||||
return
|
||||
}
|
||||
}
|
||||
} else {
|
||||
h.doSyncNicInfo(nic)
|
||||
err := h.doSyncNicInfo(nic)
|
||||
if err != nil {
|
||||
h.onFail(errors.Wrapf(err, "doSyncNicInfo %s", nic.Inter))
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
h.getStoragecacheInfo()
|
||||
}
|
||||
|
||||
func (h *SHostInfo) doUploadNicInfo(nic *SNIC) {
|
||||
log.Infof("Upload NIC br:%s if:%s", nic.Bridge, nic.Inter)
|
||||
func (h *SHostInfo) doSendPhysicalNicInfo(nic *types.SNicDevInfo) error {
|
||||
return h.doUploadNicInfoInternal(nic.Dev, nic.Mac.String(), "", "", "", nic.Up != nil && *nic.Up)
|
||||
}
|
||||
|
||||
func (h *SHostInfo) doUploadNicInfo(nic *SNIC) error {
|
||||
err := h.doUploadNicInfoInternal(nic.Inter, nic.BridgeDev.GetMac(), nic.Network, nic.Bridge, nic.Ip, true)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "doUploadNicInfoInternal")
|
||||
}
|
||||
return h.onUploadNicInfoSucc(nic)
|
||||
}
|
||||
|
||||
func (h *SHostInfo) doUploadNicInfoInternal(ifname, mac, net, bridge, ipaddr string, isUp bool) error {
|
||||
log.Infof("Upload NIC br:%s if:%s", bridge, ifname)
|
||||
content := jsonutils.NewDict()
|
||||
content.Set("mac", jsonutils.NewString(nic.BridgeDev.GetMac()))
|
||||
content.Set("wire", jsonutils.NewString(nic.Network))
|
||||
content.Set("bridge", jsonutils.NewString(nic.Bridge))
|
||||
content.Set("interface", jsonutils.NewString(nic.Inter))
|
||||
content.Set("link_up", jsonutils.JSONTrue)
|
||||
if len(nic.Ip) > 0 {
|
||||
content.Set("ip_addr", jsonutils.NewString(nic.Ip))
|
||||
if nic.Ip == h.GetMasterIp() {
|
||||
content.Set("mac", jsonutils.NewString(mac))
|
||||
content.Set("wire", jsonutils.NewString(net))
|
||||
content.Set("bridge", jsonutils.NewString(bridge))
|
||||
content.Set("interface", jsonutils.NewString(ifname))
|
||||
if isUp {
|
||||
content.Set("link_up", jsonutils.JSONTrue)
|
||||
} else {
|
||||
content.Set("link_up", jsonutils.JSONFalse)
|
||||
}
|
||||
if len(ipaddr) > 0 {
|
||||
content.Set("ip_addr", jsonutils.NewString(ipaddr))
|
||||
if ipaddr == h.GetMasterIp() {
|
||||
content.Set("nic_type", jsonutils.NewString(api.NIC_TYPE_ADMIN))
|
||||
}
|
||||
// always try to allocate from reserved pool
|
||||
@@ -1384,14 +1424,12 @@ func (h *SHostInfo) doUploadNicInfo(nic *SNIC) {
|
||||
_, err := modules.Hosts.PerformAction(h.GetSession(),
|
||||
h.HostId, "add-netif", content)
|
||||
if err != nil {
|
||||
h.onFail(err)
|
||||
return
|
||||
} else {
|
||||
h.onUploadNicInfoSucc(nic)
|
||||
return errors.Wrap(err, "modules.Hosts.PerformAction add-netif")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h *SHostInfo) doSyncNicInfo(nic *SNIC) {
|
||||
func (h *SHostInfo) doSyncNicInfo(nic *SNIC) error {
|
||||
content := jsonutils.NewDict()
|
||||
content.Set("bridge", jsonutils.NewString(nic.Bridge))
|
||||
content.Set("interface", jsonutils.NewString(nic.Inter))
|
||||
@@ -1400,16 +1438,15 @@ func (h *SHostInfo) doSyncNicInfo(nic *SNIC) {
|
||||
_, err := modules.Hostwires.Update(h.GetSession(),
|
||||
h.HostId, nic.WireId, query, content)
|
||||
if err != nil {
|
||||
h.onFail(err)
|
||||
return
|
||||
return errors.Wrap(err, "modules.Hostwires.Update")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h *SHostInfo) onUploadNicInfoSucc(nic *SNIC) {
|
||||
func (h *SHostInfo) onUploadNicInfoSucc(nic *SNIC) error {
|
||||
res, err := modules.Hostwires.Get(h.GetSession(), h.HostId, nic.Network, nil)
|
||||
if err != nil {
|
||||
h.onFail(err)
|
||||
return
|
||||
return errors.Wrap(err, "modules.Hostwires.Get")
|
||||
} else {
|
||||
bridge, _ := res.GetString("bridge")
|
||||
iface, _ := res.GetString("interface")
|
||||
@@ -1424,10 +1461,10 @@ func (h *SHostInfo) onUploadNicInfoSucc(nic *SNIC) {
|
||||
}
|
||||
nic.SetWireId(wire, wireId, bandwidth)
|
||||
} else {
|
||||
h.onFail("GetMatchNic failed!!!")
|
||||
return
|
||||
return errors.Error("GetMatchNic failed!!!")
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h *SHostInfo) getStoragecacheInfo() {
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
// 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 compute
|
||||
|
||||
import (
|
||||
"yunion.io/x/onecloud/pkg/mcclient/modulebase"
|
||||
"yunion.io/x/onecloud/pkg/mcclient/modules"
|
||||
)
|
||||
|
||||
var (
|
||||
NetTapFlows modulebase.ResourceManager
|
||||
)
|
||||
|
||||
func init() {
|
||||
NetTapFlows = modules.NewComputeManager("tap_flow", "tap_flows",
|
||||
[]string{
|
||||
"id", "name", "enabled", "type", "tap", "tap_id", "source_id", "source", "source_ips", "net_id", "net", "vlan_id", "mac_addr", "direction",
|
||||
},
|
||||
[]string{},
|
||||
)
|
||||
|
||||
modules.RegisterCompute(&NetTapFlows)
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
// 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 compute
|
||||
|
||||
import (
|
||||
"yunion.io/x/onecloud/pkg/mcclient/modulebase"
|
||||
"yunion.io/x/onecloud/pkg/mcclient/modules"
|
||||
)
|
||||
|
||||
var (
|
||||
NetTapServices modulebase.ResourceManager
|
||||
)
|
||||
|
||||
func init() {
|
||||
NetTapServices = modules.NewComputeManager("tap_service", "tap_services",
|
||||
[]string{
|
||||
"id", "name", "enabled", "type", "target_id", "target", "target_ips", "mac_addr", "flow_count",
|
||||
},
|
||||
[]string{},
|
||||
)
|
||||
|
||||
modules.RegisterCompute(&NetTapServices)
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
// 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 compute
|
||||
|
||||
import (
|
||||
"yunion.io/x/jsonutils"
|
||||
|
||||
api "yunion.io/x/onecloud/pkg/apis/compute"
|
||||
"yunion.io/x/onecloud/pkg/mcclient/options"
|
||||
)
|
||||
|
||||
type NetTapFlowCreateOptions struct {
|
||||
api.NetTapFlowCreateInput
|
||||
}
|
||||
|
||||
func (o *NetTapFlowCreateOptions) Params() (jsonutils.JSONObject, error) {
|
||||
return jsonutils.Marshal(o), nil
|
||||
}
|
||||
|
||||
type NetTapFlowListOptions struct {
|
||||
options.BaseListOptions
|
||||
|
||||
TapId string `help:"filter by tap id" json:"tap_id"`
|
||||
}
|
||||
|
||||
func (o *NetTapFlowListOptions) Params() (jsonutils.JSONObject, error) {
|
||||
return options.ListStructToParams(o)
|
||||
}
|
||||
|
||||
type NetTapFlowIdOptions struct {
|
||||
ID string `json:"-" help:"Id or name of net tap service"`
|
||||
}
|
||||
|
||||
func (o *NetTapFlowIdOptions) GetId() string {
|
||||
return o.ID
|
||||
}
|
||||
|
||||
func (o *NetTapFlowIdOptions) Params() (jsonutils.JSONObject, error) {
|
||||
return options.StructToParams(o)
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
// 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 compute
|
||||
|
||||
import (
|
||||
"yunion.io/x/jsonutils"
|
||||
|
||||
api "yunion.io/x/onecloud/pkg/apis/compute"
|
||||
"yunion.io/x/onecloud/pkg/mcclient/options"
|
||||
)
|
||||
|
||||
type NetTapServiceCreateOptions struct {
|
||||
api.NetTapServiceCreateInput
|
||||
}
|
||||
|
||||
func (o *NetTapServiceCreateOptions) Params() (jsonutils.JSONObject, error) {
|
||||
return jsonutils.Marshal(o), nil
|
||||
}
|
||||
|
||||
type NetTapServiceListOptions struct {
|
||||
options.BaseListOptions
|
||||
}
|
||||
|
||||
func (o *NetTapServiceListOptions) Params() (jsonutils.JSONObject, error) {
|
||||
return options.ListStructToParams(o)
|
||||
}
|
||||
|
||||
type NetTapServiceIdOptions struct {
|
||||
ID string `json:"-" help:"Id or name of net tap service"`
|
||||
}
|
||||
|
||||
func (o *NetTapServiceIdOptions) GetId() string {
|
||||
return o.ID
|
||||
}
|
||||
|
||||
func (o *NetTapServiceIdOptions) Params() (jsonutils.JSONObject, error) {
|
||||
return options.StructToParams(o)
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
// 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 seclib2
|
||||
|
||||
import (
|
||||
"crypto/sha256"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
func HashId(seed string, idx byte, width int) string {
|
||||
h := sha256.New()
|
||||
h.Write([]byte(seed))
|
||||
if idx > 0 {
|
||||
h.Write([]byte{idx})
|
||||
}
|
||||
sum := h.Sum(nil)
|
||||
numStr := fmt.Sprintf(fmt.Sprintf("%%0%dx", width), binary.BigEndian.Uint64(sum[:8]))
|
||||
if len(numStr) > width {
|
||||
numStr = numStr[:width]
|
||||
}
|
||||
return numStr
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
// 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 seclib2
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestHashId(t *testing.T) {
|
||||
for i := 0; i < 256; i++ {
|
||||
t.Log(HashId("test", byte(i), 6))
|
||||
}
|
||||
}
|
||||
@@ -182,6 +182,10 @@ func unloadKvmModule(name string) bool {
|
||||
|
||||
func GetKernelModuleParameter(name, moduel string) string {
|
||||
pa := path.Join("/sys/module/", strings.Replace(name, "-", "_", -1), "/parameters/", moduel)
|
||||
return GetSysConfig(pa)
|
||||
}
|
||||
|
||||
func GetSysConfig(pa string) string {
|
||||
if f, err := os.Stat(pa); err == nil {
|
||||
if f.IsDir() {
|
||||
return ""
|
||||
@@ -218,7 +222,7 @@ func SetSysConfig(cpath, val string) bool {
|
||||
log.Errorln(err)
|
||||
return false
|
||||
}
|
||||
if string(oval) != val {
|
||||
if strings.TrimSpace(string(oval)) != val {
|
||||
err = fileutils2.FilePutContents(cpath, val, false)
|
||||
if err == nil {
|
||||
return true
|
||||
|
||||
@@ -0,0 +1,76 @@
|
||||
// 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 sysutils
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
|
||||
"yunion.io/x/log"
|
||||
"yunion.io/x/pkg/errors"
|
||||
|
||||
"yunion.io/x/onecloud/pkg/cloudcommon/types"
|
||||
"yunion.io/x/onecloud/pkg/httperrors"
|
||||
)
|
||||
|
||||
const (
|
||||
sysNetPath = "/sys/class/net"
|
||||
)
|
||||
|
||||
func Nics() ([]*types.SNicDevInfo, error) {
|
||||
if _, err := os.Stat(sysNetPath); !os.IsNotExist(err) {
|
||||
nicDevs, err := ioutil.ReadDir(sysNetPath)
|
||||
if err != nil {
|
||||
log.Errorf("ReadDir %s error: %s", sysNetPath, err)
|
||||
return nil, errors.Wrapf(err, "ioutil.ReadDir(%s)", sysNetPath)
|
||||
}
|
||||
nics := make([]*types.SNicDevInfo, 0)
|
||||
for _, nic := range nicDevs {
|
||||
netPath := filepath.Join(sysNetPath, nic.Name())
|
||||
if _, err := os.Stat(filepath.Join(netPath, "device")); os.IsNotExist(err) {
|
||||
continue
|
||||
}
|
||||
speedStr := GetSysConfig(filepath.Join(netPath, "speed"))
|
||||
speed := 0
|
||||
if len(speedStr) > 0 {
|
||||
speed, _ = strconv.Atoi(speedStr)
|
||||
}
|
||||
carrier := GetSysConfig(filepath.Join(netPath, "carrier"))
|
||||
up := false
|
||||
if carrier == "1" {
|
||||
up = true
|
||||
}
|
||||
mac, _ := net.ParseMAC(GetSysConfig(filepath.Join(netPath, "address")))
|
||||
mtuStr := GetSysConfig(filepath.Join(netPath, "mtu"))
|
||||
mtu := 0
|
||||
if len(mtuStr) > 0 {
|
||||
mtu, _ = strconv.Atoi(mtuStr)
|
||||
}
|
||||
nicInfo := &types.SNicDevInfo{
|
||||
Dev: nic.Name(),
|
||||
Mac: mac,
|
||||
Speed: speed,
|
||||
Up: &up,
|
||||
Mtu: mtu,
|
||||
}
|
||||
nics = append(nics, nicInfo)
|
||||
}
|
||||
return nics, nil
|
||||
}
|
||||
return nil, errors.Wrapf(httperrors.ErrNotSupported, "no such dir %s", sysNetPath)
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
// 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 sysutils
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"yunion.io/x/jsonutils"
|
||||
)
|
||||
|
||||
func TestNics(t *testing.T) {
|
||||
nics, err := Nics()
|
||||
if err != nil {
|
||||
t.Errorf("error %s", err)
|
||||
} else {
|
||||
t.Logf("%s", jsonutils.Marshal(nics))
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user