Merge pull request #4256 from yousong/feature/yousong-vpc

Feature/yousong vpc
This commit is contained in:
yunion-ci-robot
2020-01-19 11:47:11 +08:00
committed by GitHub
66 changed files with 7290 additions and 388 deletions
+71
View File
@@ -0,0 +1,71 @@
// 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 main
import (
"context"
"os"
"os/signal"
"sync"
"syscall"
"yunion.io/x/log"
app_common "yunion.io/x/onecloud/pkg/cloudcommon/app"
common_options "yunion.io/x/onecloud/pkg/cloudcommon/options"
"yunion.io/x/onecloud/pkg/util/atexit"
"yunion.io/x/onecloud/pkg/vpcagent/options"
_ "yunion.io/x/onecloud/pkg/vpcagent/ovn"
"yunion.io/x/onecloud/pkg/vpcagent/worker"
)
func main() {
defer atexit.Handle()
opts := &options.Options{}
commonOpts := &opts.CommonOptions
{
common_options.ParseOptions(opts, os.Args, "vpcagent.conf", "vpcagent")
app_common.InitAuth(commonOpts, func() {
log.Infof("auth finished ok")
})
}
if err := opts.ValidateThenInit(); err != nil {
log.Fatalf("opts validate: %s", err)
}
w := worker.NewWorker(opts)
if w == nil {
log.Fatalf("new worker failed")
}
{
wg := &sync.WaitGroup{}
ctx, cancelFunc := context.WithCancel(context.Background())
ctx = context.WithValue(ctx, "wg", wg)
wg.Add(1)
go w.Start(ctx)
go func() {
sigChan := make(chan os.Signal)
signal.Notify(sigChan, syscall.SIGINT)
signal.Notify(sigChan, syscall.SIGTERM)
sig := <-sigChan
log.Infof("signal received: %s", sig)
cancelFunc()
}()
wg.Wait()
}
}
+1 -1
View File
@@ -187,7 +187,7 @@ func (f *ResourceHandlers) listHandler(ctx context.Context, w http.ResponseWrite
}
}
func (f *ResourceHandlers) doList(session *mcclient.ClientSession, module modulebase.BaseManagerInterface, query jsonutils.JSONObject, w http.ResponseWriter, r *http.Request) {
func (f *ResourceHandlers) doList(session *mcclient.ClientSession, module modulebase.IBaseManager, query jsonutils.JSONObject, w http.ResponseWriter, r *http.Request) {
var exportKeys []string
var exportTexts []string
exportFormat, _ := query.GetString("export")
+4
View File
@@ -70,3 +70,7 @@ type WireListInput struct {
type GlobalVpcListInput struct {
apis.EnabledStatusStandaloneResourceListInput
}
const (
VPC_PROVIDER_OVN = "ovn"
)
+38 -7
View File
@@ -238,22 +238,26 @@ func ListItemQueryFilters(manager IModelManager,
return listItemQueryFilters(manager, ctx, q, userCred, query, action, false)
}
func listItemQueryFilters(manager IModelManager,
func listItemQueryFiltersRaw(manager IModelManager,
ctx context.Context, q *sqlchemy.SQuery,
userCred mcclient.TokenCredential,
query jsonutils.JSONObject,
action string,
doCheckRbac bool,
useRawQuery bool,
) (*sqlchemy.SQuery, error) {
ownerId, queryScope, err := FetchCheckQueryOwnerScope(ctx, userCred, query, manager, action, doCheckRbac)
if err != nil {
return nil, httperrors.NewGeneralError(err)
}
q = manager.FilterByOwner(q, ownerId, queryScope)
// apply all filters
q = manager.FilterBySystemAttributes(q, userCred, query, queryScope)
q = manager.FilterByHiddenSystemAttributes(q, userCred, query, queryScope)
if !useRawQuery {
// Specifically for joint resource, these filters will exclude
// deleted resources by joining with master/slave tables
q = manager.FilterByOwner(q, ownerId, queryScope)
q = manager.FilterBySystemAttributes(q, userCred, query, queryScope)
q = manager.FilterByHiddenSystemAttributes(q, userCred, query, queryScope)
}
q, err = ListItemFilter(manager, ctx, q, userCred, query)
if err != nil {
@@ -288,6 +292,16 @@ func listItemQueryFilters(manager IModelManager,
return q, nil
}
func listItemQueryFilters(manager IModelManager,
ctx context.Context, q *sqlchemy.SQuery,
userCred mcclient.TokenCredential,
query jsonutils.JSONObject,
action string,
doCheckRbac bool,
) (*sqlchemy.SQuery, error) {
return listItemQueryFiltersRaw(manager, ctx, q, userCred, query, action, doCheckRbac, false)
}
func mergeFields(metaFields, queryFields []string, isSysAdmin bool) stringutils2.SSortedStrings {
meta := stringutils2.NewSortedStrings(metaFields)
if len(queryFields) == 0 {
@@ -448,7 +462,24 @@ func ListItems(manager IModelManager, ctx context.Context, userCred mcclient.Tok
limit, _ := query.Int("limit")
offset, _ := query.Int("offset")
pagingMarker, _ := query.GetString("paging_marker")
q := manager.Query()
var (
q *sqlchemy.SQuery
useRawQuery bool
)
{
// query senders are responsible for clear up other constraint
// like setting "pendinge_delete" to "all"
queryDelete, _ := query.GetString("delete")
if queryDelete == "all" && userCred.HasSystemAdminPrivilege() {
useRawQuery = true
}
}
if useRawQuery {
q = manager.RawQuery()
} else {
q = manager.Query()
}
queryDict, ok := query.(*jsonutils.JSONDict)
if !ok {
@@ -467,7 +498,7 @@ func ListItems(manager IModelManager, ctx context.Context, userCred mcclient.Tok
return nil, err
}
q, err = listItemQueryFilters(manager, ctx, q, userCred, queryDict, policy.PolicyActionList, true)
q, err = listItemQueryFiltersRaw(manager, ctx, q, userCred, queryDict, policy.PolicyActionList, true, useRawQuery)
if err != nil {
return nil, err
}
+5
View File
@@ -63,6 +63,7 @@ type IModelManager interface {
// fetch hook
Query(val ...string) *sqlchemy.SQuery
RawQuery(val ...string) *sqlchemy.SQuery
FilterById(q *sqlchemy.SQuery, idStr string) *sqlchemy.SQuery
FilterByNotId(q *sqlchemy.SQuery, idStr string) *sqlchemy.SQuery
@@ -129,6 +130,9 @@ type IModel interface {
object.IObject
GetName() string
GetUpdateVersion() int
GetUpdatedAt() time.Time
GetDeleted() bool
KeywordPlural() string
@@ -283,6 +287,7 @@ type IVirtualModelManager interface {
type IVirtualModel interface {
IStandaloneModel
IPendingDeletable
IsOwner(userCred mcclient.TokenCredential) bool
// IsAdmin(userCred mcclient.TokenCredential) bool
+6 -2
View File
@@ -156,7 +156,9 @@ func JointMaster(joint IJointModel) IStandaloneModel { // need override
//log.Debugf("MasterID: %s %s", masterId, masterMan.KeywordPlural())
if len(masterId) > 0 {
master, _ := masterMan.FetchById(masterId)
return master.(IStandaloneModel)
if master != nil {
return master.(IStandaloneModel)
}
}
return nil
}
@@ -167,7 +169,9 @@ func JointSlave(joint IJointModel) IStandaloneModel { // need override
//log.Debugf("SlaveID: %s %s", slaveId, slaveMan.KeywordPlural())
if len(slaveId) > 0 {
slave, _ := slaveMan.FetchById(slaveId)
return slave.(IStandaloneModel)
if slave != nil {
return slave.(IStandaloneModel)
}
}
return nil
}
+16
View File
@@ -149,6 +149,10 @@ func (manager *SModelBaseManager) Query(fieldNames ...string) *sqlchemy.SQuery {
return instance.Query(fields...)
}
func (manager *SModelBaseManager) RawQuery(fieldNames ...string) *sqlchemy.SQuery {
return manager.Query(fieldNames...)
}
func (manager *SModelBaseManager) FilterById(q *sqlchemy.SQuery, idStr string) *sqlchemy.SQuery {
return q
}
@@ -412,6 +416,18 @@ func (model *SModelBase) GetName() string {
return ""
}
func (model *SModelBase) GetUpdatedAt() time.Time {
return time.Time{}
}
func (model *SModelBase) GetUpdateVersion() int {
return 0
}
func (model *SModelBase) GetDeleted() bool {
return false
}
func (model *SModelBase) SetModelManager(man IModelManager, virtual IModel) {
model.manager = man
model.SetVirtualObject(virtual)
+8
View File
@@ -329,6 +329,14 @@ func (opslog *SOpsLog) GetName() string {
return fmt.Sprintf("%s-%s", opslog.ObjType, opslog.Action)
}
func (opslog *SOpsLog) GetUpdatedAt() time.Time {
return opslog.OpsTime
}
func (opslog *SOpsLog) GetUpdateVersion() int {
return 1
}
func (opslog *SOpsLog) GetModelManager() IModelManager {
return OpsLog
}
+13 -1
View File
@@ -34,7 +34,7 @@ type SResourceBase struct {
UpdatedAt time.Time `nullable:"false" updated_at:"true" list:"user"`
UpdateVersion int `default:"0" nullable:"false" auto_version:"true" list:"user"`
DeletedAt time.Time ``
Deleted bool `nullable:"false" default:"false"`
Deleted bool `nullable:"false" default:"false" list:"admin"`
}
type SResourceBaseManager struct {
@@ -128,3 +128,15 @@ func (manager *SResourceBaseManager) ListItemFilter(ctx context.Context, q *sqlc
}
return q, nil
}
func (model *SResourceBase) GetUpdateVersion() int {
return model.UpdateVersion
}
func (model *SResourceBase) GetUpdatedAt() time.Time {
return model.UpdatedAt
}
func (model *SResourceBase) GetDeleted() bool {
return model.Deleted
}
+1 -1
View File
@@ -53,7 +53,7 @@ func (self *SVirtualizedGuestDriver) PrepareDiskRaidConfig(userCred mcclient.Tok
}
func (self *SVirtualizedGuestDriver) GetNamedNetworkConfiguration(guest *models.SGuest, ctx context.Context, userCred mcclient.TokenCredential, host *models.SHost, netConfig *api.NetworkConfig) (*models.SNetwork, []models.SNicConfig, api.IPAllocationDirection, bool) {
net, _ := host.GetNetworkWithIdAndCredential(netConfig.Network, userCred, netConfig.Reserved)
net, _ := host.GetNetworkWithId(netConfig.Network, netConfig.Reserved)
nicConfs := []models.SNicConfig{
{
Mac: netConfig.Mac,
-1
View File
@@ -758,7 +758,6 @@ func (self *SCloudprovider) GetCloudaccount() *SCloudaccount {
func (manager *SCloudproviderManager) FetchCloudproviderById(providerId string) *SCloudprovider {
providerObj, err := manager.FetchById(providerId)
if err != nil {
log.Errorf("fetch cloud provider %s: %s", providerId, err)
return nil
}
return providerObj.(*SCloudprovider)
+26 -9
View File
@@ -337,7 +337,7 @@ func (self *SGuestnetwork) GetTeamGuestnetwork() (*SGuestnetwork, error) {
func (self *SGuestnetwork) getJsonDescAtBaremetal(host *SHost) jsonutils.JSONObject {
network := self.GetNetwork()
hostwire := host.getHostwireOfIdAndMac(network.WireId, self.MacAddr)
return self.getGeneralJsonDesc(host, network, hostwire)
return self.getJsonDescHostwire(network, hostwire)
}
func guestGetHostWireFromNetwork(host *SHost, network *SNetwork) (*SHostwire, error) {
@@ -360,14 +360,34 @@ func guestGetHostWireFromNetwork(host *SHost, network *SNetwork) (*SHostwire, er
func (self *SGuestnetwork) getJsonDescAtHost(host *SHost) jsonutils.JSONObject {
network := self.GetNetwork()
hostWire, err := guestGetHostWireFromNetwork(host, network)
if err != nil {
log.Errorln(err)
if network.isOneCloudVpcNetwork() {
return self.getJsonDescOneCloudVpc(network)
} else {
hostWire, err := guestGetHostWireFromNetwork(host, network)
if err != nil {
log.Errorln(err)
}
return self.getJsonDescHostwire(network, hostWire)
}
return self.getGeneralJsonDesc(host, network, hostWire)
}
func (self *SGuestnetwork) getGeneralJsonDesc(host *SHost, network *SNetwork, hostwire *SHostwire) jsonutils.JSONObject {
func (self *SGuestnetwork) getJsonDescHostwire(network *SNetwork, hostwire *SHostwire) *jsonutils.JSONDict {
desc := self.getJsonDesc(network)
desc.Add(jsonutils.NewString(hostwire.Bridge), "bridge")
desc.Add(jsonutils.NewString(hostwire.WireId), "wire_id")
desc.Add(jsonutils.NewString(hostwire.Interface), "interface")
return desc
}
func (self *SGuestnetwork) getJsonDescOneCloudVpc(network *SNetwork) *jsonutils.JSONDict {
vpcDesc := jsonutils.NewDict()
vpcDesc.Set("provider", jsonutils.NewString(api.VPC_PROVIDER_OVN))
desc := self.getJsonDesc(network)
desc.Set("vpc", vpcDesc)
return desc
}
func (self *SGuestnetwork) getJsonDesc(network *SNetwork) *jsonutils.JSONDict {
desc := jsonutils.NewDict()
desc.Add(jsonutils.NewString(network.Name), "net")
@@ -400,10 +420,7 @@ func (self *SGuestnetwork) getGeneralJsonDesc(host *SHost, network *SNetwork, ho
desc.Add(jsonutils.NewString(self.GetIfname()), "ifname")
desc.Add(jsonutils.NewInt(int64(network.GuestIpMask)), "masklen")
desc.Add(jsonutils.NewString(self.Driver), "driver")
desc.Add(jsonutils.NewString(hostwire.Bridge), "bridge")
desc.Add(jsonutils.NewString(hostwire.WireId), "wire_id")
desc.Add(jsonutils.NewInt(int64(network.VlanId)), "vlan")
desc.Add(jsonutils.NewString(hostwire.Interface), "interface")
desc.Add(jsonutils.NewInt(int64(self.getBandwidth())), "bw")
desc.Add(jsonutils.NewInt(int64(self.getMtu())), "mtu")
desc.Add(jsonutils.NewInt(int64(self.Index)), "index")
+30 -10
View File
@@ -124,7 +124,8 @@ type SHost struct {
HostType string `width:"36" charset:"ascii" nullable:"false" list:"admin" update:"admin" create:"admin_required"` // Column(VARCHAR(36, charset='ascii'), nullable=False)
Version string `width:"64" charset:"ascii" list:"admin" update:"admin" create:"admin_optional"` // Column(VARCHAR(64, charset='ascii'))
Version string `width:"64" charset:"ascii" list:"admin" update:"admin" create:"admin_optional"` // Column(VARCHAR(64, charset='ascii'))
OvnVersion string `width:"64" charset:"ascii" list:"admin" update:"admin" create:"admin_optional"`
IsBaremetal bool `nullable:"true" default:"false" list:"admin" update:"admin" create:"admin_optional"` // Column(Boolean, nullable=True, default=False)
@@ -1983,16 +1984,35 @@ func (self *SHost) GetNetinterfacesWithIdAndCredential(netId string, userCred mc
return nil, nil
}
func (self *SHost) GetNetworkWithIdAndCredential(netId string, userCred mcclient.TokenCredential, reserved bool) (*SNetwork, error) {
networks := NetworkManager.Query().SubQuery()
hostwires := HostwireManager.Query().SubQuery()
hosts := HostManager.Query().SubQuery()
func (self *SHost) GetNetworkWithId(netId string, reserved bool) (*SNetwork, error) {
var q1, q2 *sqlchemy.SQuery
{
networks := NetworkManager.Query()
hostwires := HostwireManager.Query().SubQuery()
hosts := HostManager.Query().SubQuery()
q1 = networks
q1 = q1.Join(hostwires, sqlchemy.Equals(hostwires.Field("wire_id"), networks.Field("wire_id")))
q1 = q1.Join(hosts, sqlchemy.Equals(hosts.Field("id"), hostwires.Field("host_id")))
q1 = q1.Filter(sqlchemy.Equals(networks.Field("id"), netId))
q1 = q1.Filter(sqlchemy.Equals(hosts.Field("id"), self.Id))
}
{
networks := NetworkManager.Query()
wires := WireManager.Query().SubQuery()
vpcs := VpcManager.Query().SubQuery()
regions := CloudregionManager.Query().SubQuery()
q2 = networks
q2 = q2.Join(wires, sqlchemy.Equals(wires.Field("id"), networks.Field("wire_id")))
q2 = q2.Join(vpcs, sqlchemy.Equals(vpcs.Field("id"), wires.Field("vpc_id")))
q2 = q2.Join(regions, sqlchemy.Equals(regions.Field("id"), vpcs.Field("cloudregion_id")))
q2 = q2.Filter(sqlchemy.Equals(networks.Field("id"), netId))
q2 = q2.Filter(sqlchemy.AND(
sqlchemy.Equals(regions.Field("provider"), api.CLOUD_PROVIDER_ONECLOUD),
sqlchemy.NOT(sqlchemy.Equals(vpcs.Field("id"), api.DEFAULT_VPC_ID)),
))
}
q := networks.Query()
q = q.Join(hostwires, sqlchemy.Equals(hostwires.Field("wire_id"), networks.Field("wire_id")))
q = q.Join(hosts, sqlchemy.Equals(hosts.Field("id"), hostwires.Field("host_id")))
q = q.Filter(sqlchemy.Equals(hosts.Field("id"), self.Id))
q = q.Filter(sqlchemy.Equals(networks.Field("id"), netId))
q := sqlchemy.Union(q1, q2).Query()
net := SNetwork{}
net.SetModelManager(NetworkManager, &net)
+199 -164
View File
@@ -989,7 +989,9 @@ func (self *SNetwork) getMoreDetails(ctx context.Context, extra *jsonutils.JSOND
extra.Add(jsonutils.NewString(zone.Name), "zone")
extra.Add(jsonutils.NewString(zone.Id), "zone_id")
}
extra.Add(jsonutils.NewString(wire.Name), "wire")
if wire != nil {
extra.Add(jsonutils.NewString(wire.Name), "wire")
}
if self.IsExitNetwork() {
extra.Add(jsonutils.JSONTrue, "exit")
} else {
@@ -1018,14 +1020,14 @@ func (self *SNetwork) getMoreDetails(ctx context.Context, extra *jsonutils.JSOND
if len(vpc.GetExternalId()) > 0 {
extra.Add(jsonutils.NewString(vpc.GetExternalId()), "vpc_ext_id")
}
info := vpc.getCloudProviderInfo()
extra.Update(jsonutils.Marshal(&info))
}
routes := self.GetRoutes()
if len(routes) > 0 {
extra.Add(jsonutils.Marshal(routes), "routes")
}
info := vpc.getCloudProviderInfo()
extra.Update(jsonutils.Marshal(&info))
extra = GetSchedtagsDetailsToResource(self, ctx, extra)
return extra
@@ -1250,52 +1252,115 @@ func (manager *SNetworkManager) newIfnameHint(hint string) (string, error) {
return r, nil
}
func (manager *SNetworkManager) ValidateCreateData(ctx context.Context, userCred mcclient.TokenCredential, ownerId mcclient.IIdentityProvider, query jsonutils.JSONObject, input api.NetworkCreateInput) (api.NetworkCreateInput, error) {
var err error
var startIp, endIp netutils.IPV4Addr
if len(input.GuestIpPrefix) > 0 {
prefix, err := netutils.NewIPV4Prefix(input.GuestIpPrefix)
if err != nil {
return input, httperrors.NewInputParameterError("ip_prefix error: %s", err)
}
iprange := prefix.ToIPRange()
startIp = iprange.StartIp().StepUp()
endIp = iprange.EndIp().StepDown()
input.GuestIpMask = int64(prefix.MaskLen)
// 根据掩码得到合法的GuestIpPrefix
input.GuestIpPrefix = prefix.String()
} else {
startIp, err = netutils.NewIPV4Addr(input.GuestIpStart)
if err != nil {
return input, httperrors.NewInputParameterError("Invalid start ip: %s %s", input.GuestIpStart, err)
}
endIp, err = netutils.NewIPV4Addr(input.GuestIpEnd)
if err != nil {
return input, httperrors.NewInputParameterError("invalid end ip: %s %s", input.GuestIpEnd, err)
}
if startIp > endIp {
tmp := startIp
startIp = endIp
endIp = tmp
}
func (manager *SNetworkManager) validateEnsureWire(ctx context.Context, userCred mcclient.TokenCredential, input api.NetworkCreateInput) (w *SWire, v *SVpc, cr *SCloudregion, err error) {
wObj, err := WireManager.FetchByIdOrName(userCred, input.Wire)
if err != nil {
err = errors.Wrapf(err, "wire %s", input.Wire)
return
}
input.GuestIpStart = startIp.String()
input.GuestIpEnd = endIp.String()
w = wObj.(*SWire)
v = w.getVpc()
crObj, err := CloudregionManager.FetchById(v.CloudregionId)
if err != nil {
err = errors.Wrapf(err, "cloudregion %s", v.CloudregionId)
return
}
cr = crObj.(*SCloudregion)
return
}
if !isValidMaskLen(input.GuestIpMask) {
return input, httperrors.NewInputParameterError("Invalid masklen %d", input.GuestIpMask)
func (manager *SNetworkManager) validateEnsureZoneVpc(ctx context.Context, userCred mcclient.TokenCredential, input api.NetworkCreateInput) (w *SWire, v *SVpc, cr *SCloudregion, err error) {
zObj, err := ZoneManager.FetchByIdOrName(userCred, input.Zone)
if err != nil {
err = errors.Wrapf(err, "zone %s", input.Zone)
return
}
z := zObj.(*SZone)
vObj, err := VpcManager.FetchByIdOrName(userCred, input.Vpc)
if err != nil {
err = errors.Wrapf(err, "vpc %s", input.Vpc)
return
}
v = vObj.(*SVpc)
var wires []SWire
// 华为云,ucloud wire zone_id 为空
cr = z.GetRegion()
if utils.IsInStringArray(cr.Provider, api.REGIONAL_NETWORK_PROVIDERS) {
wires, err = WireManager.getWiresByVpcAndZone(v, nil)
} else {
wires, err = WireManager.getWiresByVpcAndZone(v, z)
}
if err != nil {
return
} else if len(wires) > 1 {
err = httperrors.NewConflictError("found %d wires for zone %s and vpc %s", len(wires), input.Zone, input.Vpc)
return
} else if len(wires) == 1 {
w = &wires[0]
return
}
// wire not found. We auto create one for OneCloud vpc
if cr.Provider == api.CLOUD_PROVIDER_ONECLOUD {
w, err = v.initWire(ctx, z)
if err != nil {
err = errors.Wrapf(err, "vpc %s init wire", v.Id)
return
}
return
}
err = httperrors.NewNotFoundError("wire not found for zone %s and vpc %s", input.Zone, input.Vpc)
return
}
func (manager *SNetworkManager) ValidateCreateData(ctx context.Context, userCred mcclient.TokenCredential, ownerId mcclient.IIdentityProvider, query jsonutils.JSONObject, input api.NetworkCreateInput) (api.NetworkCreateInput, error) {
if input.ServerType == "" {
input.ServerType = api.NETWORK_TYPE_GUEST
} else if !utils.IsInStringArray(input.ServerType, ALL_NETWORK_TYPES) {
return input, httperrors.NewInputParameterError("Invalid server_type: %s", input.ServerType)
}
{
if len(input.IfnameHint) == 0 {
input.IfnameHint = input.Name
}
var err error
input.IfnameHint, err = manager.newIfnameHint(input.IfnameHint)
if err != nil {
return input, httperrors.NewBadRequestError("cannot derive valid ifname hint: %v", err)
}
}
var (
ipRange netutils.IPV4AddrRange
)
if len(input.GuestIpPrefix) > 0 {
prefix, err := netutils.NewIPV4Prefix(input.GuestIpPrefix)
if err != nil {
return input, httperrors.NewInputParameterError("ip_prefix error: %s", err)
}
ipRange = prefix.ToIPRange()
input.GuestIpMask = int64(prefix.MaskLen)
// 根据掩码得到合法的GuestIpPrefix
input.GuestIpPrefix = prefix.String()
} else {
ipStart, err := netutils.NewIPV4Addr(input.GuestIpStart)
if err != nil {
return input, httperrors.NewInputParameterError("Invalid start ip: %s %s", input.GuestIpStart, err)
}
ipEnd, err := netutils.NewIPV4Addr(input.GuestIpEnd)
if err != nil {
return input, httperrors.NewInputParameterError("invalid end ip: %s %s", input.GuestIpEnd, err)
}
ipRange = netutils.NewIPV4AddrRange(ipStart, ipEnd)
}
if !isValidMaskLen(input.GuestIpMask) {
return input, httperrors.NewInputParameterError("Invalid masklen %d", input.GuestIpMask)
}
for key, ipStr := range map[string]string{"guest_gateway": input.GuestGateway, "guest_dns": input.GuestDns, "guest_dhcp": input.GuestDHCP} {
if len(ipStr) > 0 {
if key == "guest_dhcp" {
@@ -1311,119 +1376,86 @@ func (manager *SNetworkManager) ValidateCreateData(ctx context.Context, userCred
}
}
nets := manager.getAllNetworks("")
if nets == nil {
return input, httperrors.NewInternalServerError("query all networks fail")
}
if isOverlapNetworks(nets, startIp, endIp) {
return input, httperrors.NewInputParameterError("Conflict address space with existing networks")
}
if len(input.WireId) > 0 {
var (
wire *SWire
vpc *SVpc
region *SCloudregion
err error
)
if input.WireId != "" {
input.Wire = input.WireId
}
if len(input.Wire) > 0 {
wireObj, err := WireManager.FetchByIdOrName(userCred, input.Wire)
if input.Wire != "" {
wire, vpc, region, err = manager.validateEnsureWire(ctx, userCred, input)
if err != nil {
if err == sql.ErrNoRows {
return input, httperrors.NewNotFoundError("wire %s not found", input.Wire)
} else {
return input, httperrors.NewInternalServerError("query wire %s error %s", input.Wire, err)
}
return input, err
}
} else if input.Zone != "" && input.Vpc != "" {
wire, vpc, region, err = manager.validateEnsureZoneVpc(ctx, userCred, input)
if err != nil {
return input, err
}
input.WireId = wireObj.GetId()
} else {
if len(input.Zone) > 0 {
if len(input.Vpc) > 0 {
zoneObj, err := ZoneManager.FetchByIdOrName(userCred, input.Zone)
if err != nil {
if err == sql.ErrNoRows {
return input, httperrors.NewNotFoundError("zone %s not found", input.Zone)
} else {
return input, httperrors.NewInternalServerError("query zone %s error %s", input.Zone, err)
}
}
vpcObj, err := VpcManager.FetchByIdOrName(userCred, input.Vpc)
if err != nil {
if err == sql.ErrNoRows {
return input, httperrors.NewNotFoundError("vpc %s not found", input.Vpc)
} else {
return input, httperrors.NewInternalServerError("query vpc %s error %s", input.Vpc, err)
}
}
vpc := vpcObj.(*SVpc)
zone := zoneObj.(*SZone)
region := zone.GetRegion()
if region == nil {
return input, httperrors.NewInternalServerError("zone %s related region not found", zone.Id)
}
// 华为云,ucloud wire zone_id 为空
var wires []SWire
if utils.IsInStringArray(region.Provider, api.REGIONAL_NETWORK_PROVIDERS) {
wires, err = WireManager.getWiresByVpcAndZone(vpc, nil)
} else {
wires, err = WireManager.getWiresByVpcAndZone(vpc, zone)
}
if err != nil {
return input, httperrors.NewInternalServerError("query wire for zone %s and vpc %s: %v", input.Zone, input.Vpc, err)
}
if len(wires) == 0 {
return input, httperrors.NewNotFoundError("wire not found for zone %s and vpc %s", input.Zone, input.Vpc)
} else if len(wires) > 1 {
return input, httperrors.NewConflictError("found %d wires for zone %s and vpc %s", len(wires), input.Zone, input.Vpc)
} else {
input.WireId = wires[0].Id
}
} else {
return input, httperrors.NewInputParameterError("No either wire or vpc provided")
}
} else {
return input, httperrors.NewInvalidStatusError("No either wire or zone provided")
}
return input, httperrors.NewInputParameterError("zone and vpc info required when wire is absent")
}
if len(input.WireId) == 0 {
return input, httperrors.NewMissingParameterError("wire_id")
}
wire := WireManager.FetchWireById(input.WireId)
if wire == nil {
return input, httperrors.NewResourceNotFoundError("wire %s not found", input.WireId)
}
vpc := wire.getVpc()
if vpc == nil {
return input, httperrors.NewInputParameterError("no valid vpc ???")
}
input.WireId = wire.Id
if vpc.Status != api.VPC_STATUS_AVAILABLE {
return input, httperrors.NewInvalidStatusError("VPC not ready")
}
vpcRanges := vpc.getIPRanges()
var (
ipStart = ipRange.StartIp()
ipEnd = ipRange.EndIp()
)
if region.Provider == api.CLOUD_PROVIDER_ONECLOUD && vpc.Id != api.DEFAULT_VPC_ID {
// reserve addresses for onecloud vpc networks
masklen := int8(input.GuestIpMask)
netAddr := ipStart.NetAddr(masklen)
if masklen >= 30 {
return input, httperrors.NewInputParameterError("subnet masklen should be smaller than 30")
}
if netAddr != ipEnd.NetAddr(masklen) {
return input, httperrors.NewInputParameterError("start and end ip when masked are not in the same cidr subnet")
}
gateway := netAddr.StepUp()
brdAddr := ipStart.BroadcastAddr(masklen)
// NOTE
//
// - reserve the 1st addr as gateway
// - reserve the last ip for broadcasting
// - reserve the 2nd-to-last for possible future use
//
// We do not allow split 192.168.1.0/24 into multiple ranges
// like
//
// - 192.168.1.50-192.168.1.100,
// - 192.168.1.100-192.168.1.200
//
// This could complicate gateway setting and topology
// management without much benefit to end users
ipStart = gateway.StepUp()
ipEnd = brdAddr.StepDown().StepDown()
input.GuestGateway = gateway.String()
}
netRange := netutils.NewIPV4AddrRange(startIp, endIp)
inRange := false
for _, vpcRange := range vpcRanges {
if vpcRange.ContainsRange(netRange) {
inRange = true
break
{
netRange := netutils.NewIPV4AddrRange(ipStart, ipEnd)
if !vpc.containsIPV4Range(netRange) {
return input, httperrors.NewInputParameterError("Network not in range of VPC cidrblock %s", vpc.CidrBlock)
}
}
{
nets := manager.getAllNetworks(wire.Id, "")
if nets == nil {
return input, httperrors.NewInternalServerError("query all networks fail")
}
if isOverlapNetworks(nets, ipStart, ipEnd) {
return input, httperrors.NewInputParameterError("Conflict address space with existing networks")
}
}
if !inRange {
return input, httperrors.NewInputParameterError("Network not in range of VPC cidrblock %s", vpc.CidrBlock)
}
if len(input.ServerType) == 0 {
input.ServerType = api.NETWORK_TYPE_GUEST
} else if !utils.IsInStringArray(input.ServerType, ALL_NETWORK_TYPES) {
return input, httperrors.NewInputParameterError("Invalid server_type: %s", input.ServerType)
}
input.GuestIpStart = ipStart.String()
input.GuestIpEnd = ipEnd.String()
input.SharableVirtualResourceCreateInput, err = manager.SSharableVirtualResourceBaseManager.ValidateCreateData(ctx, userCred, ownerId, query, input.SharableVirtualResourceCreateInput)
if err != nil {
return input, err
@@ -1431,7 +1463,7 @@ func (manager *SNetworkManager) ValidateCreateData(ctx context.Context, userCred
return input, nil
}
func (self *SNetwork) ValidateUpdateData(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data *jsonutils.JSONDict) (*jsonutils.JSONDict, error) {
func (self *SNetwork) validateUpdateData(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data *jsonutils.JSONDict) (*jsonutils.JSONDict, error) {
var startIp, endIp netutils.IPV4Addr
var err error
@@ -1439,10 +1471,6 @@ func (self *SNetwork) ValidateUpdateData(ctx context.Context, userCred mcclient.
ipEndStr, _ := data.GetString("guest_ip_end")
if len(ipStartStr) > 0 || len(ipEndStr) > 0 {
if self.isManaged() {
return nil, httperrors.NewForbiddenError("Cannot update a managed network")
}
if len(ipStartStr) > 0 {
startIp, err = netutils.NewIPV4Addr(ipStartStr)
if err != nil {
@@ -1466,7 +1494,7 @@ func (self *SNetwork) ValidateUpdateData(ctx context.Context, userCred mcclient.
endIp = tmp
}
nets := NetworkManager.getAllNetworks(self.Id)
nets := NetworkManager.getAllNetworks(self.WireId, self.Id)
if nets == nil {
return nil, httperrors.NewInternalServerError("query all networks fail")
}
@@ -1475,21 +1503,9 @@ func (self *SNetwork) ValidateUpdateData(ctx context.Context, userCred mcclient.
return nil, httperrors.NewInputParameterError("Conflict address space with existing networks")
}
vpc := self.GetVpc()
vpcRanges := vpc.getIPRanges()
netRange := netutils.NewIPV4AddrRange(startIp, endIp)
inRange := false
for _, vpcRange := range vpcRanges {
if vpcRange.ContainsRange(netRange) {
inRange = true
break
}
}
if !inRange {
vpc := self.GetVpc()
if !vpc.containsIPV4Range(netRange) {
return nil, httperrors.NewInputParameterError("Network not in range of VPC cidrblock %s", vpc.CidrBlock)
}
@@ -1503,14 +1519,9 @@ func (self *SNetwork) ValidateUpdateData(ctx context.Context, userCred mcclient.
data.Add(jsonutils.NewString(startIp.String()), "guest_ip_start")
data.Add(jsonutils.NewString(endIp.String()), "guest_ip_end")
}
if data.Contains("guest_ip_mask") {
if self.isManaged() {
return nil, httperrors.NewForbiddenError("Cannot update a managed network")
}
maskLen64, _ := data.Int("guest_ip_mask")
if !isValidMaskLen(maskLen64) {
return nil, httperrors.NewInputParameterError("Invalid masklen %d", maskLen64)
@@ -1520,9 +1531,6 @@ func (self *SNetwork) ValidateUpdateData(ctx context.Context, userCred mcclient.
for _, key := range []string{"guest_gateway", "guest_dns", "guest_dhcp"} {
ipStr, _ := data.GetString(key)
if len(ipStr) > 0 {
if self.isManaged() {
return nil, httperrors.NewForbiddenError("Cannot update a managed network")
}
if key == "guest_dhcp" {
ipList := strings.Split(ipStr, ",")
for _, ipstr := range ipList {
@@ -1536,13 +1544,31 @@ func (self *SNetwork) ValidateUpdateData(ctx context.Context, userCred mcclient.
}
}
return nil, nil
}
func (self *SNetwork) ValidateUpdateData(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data *jsonutils.JSONDict) (*jsonutils.JSONDict, error) {
if !self.isManaged() && !self.isOneCloudVpcNetwork() {
data.Remove("guest_ip_start")
data.Remove("guest_ip_end")
data.Remove("guest_ip_mask")
data.Remove("guest_gateway")
data.Remove("guest_dns")
data.Remove("guest_dhcp")
} else {
var err error
data, err = self.validateUpdateData(ctx, userCred, query, data)
if err != nil {
return nil, err
}
}
return self.SSharableVirtualResourceBase.ValidateUpdateData(ctx, userCred, query, data)
}
func (manager *SNetworkManager) getAllNetworks(excludeId string) []SNetwork {
func (manager *SNetworkManager) getAllNetworks(wireId, excludeId string) []SNetwork {
nets := make([]SNetwork, 0)
q := manager.Query()
q := manager.Query().Equals("wire_id", wireId)
if len(excludeId) > 0 {
q = q.NotEquals("id", excludeId)
}
@@ -1668,6 +1694,15 @@ func (self *SNetwork) isManaged() bool {
}
}
func (self *SNetwork) isOneCloudVpcNetwork() bool {
vpc := self.getVpc()
region := self.getRegion()
if region.Provider == api.CLOUD_PROVIDER_ONECLOUD && vpc.Id != api.DEFAULT_VPC_ID {
return true
}
return false
}
func parseIpToIntArray(ip string) ([]int, error) {
ipSp := strings.Split(strings.Trim(ip, "."), ".")
if len(ipSp) > 4 {
+29 -2
View File
@@ -614,7 +614,7 @@ func (manager *SVpcManager) ValidateCreateData(ctx context.Context, userCred mcc
}
data.Add(jsonutils.NewString(managerObj.GetId()), "manager_id")
} else {
return nil, httperrors.NewNotImplementedError("Cannot create VPC in private cloud")
data.Set("status", jsonutils.NewString(api.VPC_STATUS_AVAILABLE))
}
cidrBlock, _ := data.GetString("cidr_block")
@@ -701,7 +701,7 @@ func (self *SVpc) Delete(ctx context.Context, userCred mcclient.TokenCredential)
}
func (self *SVpc) CustomizeDelete(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) error {
if len(self.ExternalId) > 0 {
if self.Id != api.DEFAULT_VPC_ID {
return self.StartDeleteVpcTask(ctx, userCred)
} else {
return self.RealDelete(ctx, userCred)
@@ -766,6 +766,16 @@ func (self *SVpc) getIPRanges() []netutils.IPV4AddrRange {
return ret
}
func (self *SVpc) containsIPV4Range(a netutils.IPV4AddrRange) bool {
ranges := self.getIPRanges()
for i := range ranges {
if ranges[i].ContainsRange(a) {
return true
}
}
return false
}
func (self *SVpc) AllowPerformPurge(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) bool {
return db.IsAdminAllowPerform(userCred, self, "purge")
}
@@ -927,3 +937,20 @@ func (vpc *SVpc) GetGlobalVpc() (*SGlobalVpc, error) {
}
return gv.(*SGlobalVpc), nil
}
func (self *SVpc) initWire(ctx context.Context, zone *SZone) (*SWire, error) {
wire := &SWire{
VpcId: self.Id,
ZoneId: zone.Id,
Bandwidth: 10000,
Mtu: 1500,
}
wire.IsEmulated = true
wire.Name = fmt.Sprintf("vpc-%s", self.Name)
wire.SetModelManager(WireManager, wire)
err := WireManager.TableSpec().Insert(wire)
if err != nil {
return nil, err
}
return wire, nil
}
+17
View File
@@ -937,3 +937,20 @@ func (self *SWire) getMoreDetails(extra *jsonutils.JSONDict) *jsonutils.JSONDict
}
return extra
}
func (man *SWireManager) removeWiresByVpc(ctx context.Context, userCred mcclient.TokenCredential, vpc *SVpc) error {
wires := []SWire{}
q := man.Query().Equals("vpc_id", vpc.Id)
err := db.FetchModelObjects(man, q, &wires)
if err != nil {
return err
}
var errs []error
for i := range wires {
wire := &wires[i]
if err := wire.Delete(ctx, userCred); err != nil {
errs = append(errs, err)
}
}
return errors.NewAggregate(errs)
}
+6
View File
@@ -32,6 +32,7 @@ import (
"yunion.io/x/onecloud/pkg/compute/options"
"yunion.io/x/onecloud/pkg/httperrors"
"yunion.io/x/onecloud/pkg/mcclient"
"yunion.io/x/onecloud/pkg/util/choices"
"yunion.io/x/onecloud/pkg/util/rand"
)
@@ -792,6 +793,11 @@ func (self *SKVMRegionDriver) RequestDeleteLoadbalancerListenerRule(ctx context.
}
func (self *SKVMRegionDriver) ValidateCreateVpcData(ctx context.Context, userCred mcclient.TokenCredential, data *jsonutils.JSONDict) (*jsonutils.JSONDict, error) {
cidrChoices := choices.NewChoices("192.168.0.0/16", "10.0.0.0/8", "172.16.0.0/12")
cidrV := validators.NewStringChoicesValidator("cidr_block", cidrChoices)
if err := cidrV.Validate(data); err != nil {
return nil, err
}
return data, nil
}
+12
View File
@@ -785,6 +785,18 @@ func (s *SKVMGuestInstance) SaveDesc(desc jsonutils.JSONObject) error {
if !ok {
return fmt.Errorf("Unknown desc format, not JSONDict")
}
{
// fill in ovn vpc nic bridge field
nics, _ := s.Desc.GetArray("nics")
ovnBridge := options.HostOptions.OvnIntegrationBridge
for _, nic := range nics {
vpcProvider, _ := nic.GetString("vpc", "provider")
if vpcProvider == compute.VPC_PROVIDER_OVN {
nicjd := nic.(*jsonutils.JSONDict)
nicjd.Set("bridge", jsonutils.NewString(ovnBridge))
}
}
}
if err := fileutils2.FilePutContents(s.GetDescFilePath(), desc.String(), false); err != nil {
log.Errorln(err)
}
+20
View File
@@ -0,0 +1,20 @@
// 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 hostbridge
const (
DRV_OPEN_VSWITCH = "openvswitch"
DRV_LINUX_BRIDGE = "linux_bridge"
)
@@ -328,27 +328,27 @@ func (d *SBaseBridgeDriver) WarmupConfig() error {
}
func NewDriver(bridgeDriver, bridge, inter, ip string) (IBridgeDriver, error) {
if bridgeDriver == "openvswitch" {
if bridgeDriver == DRV_OPEN_VSWITCH {
return NewOVSBridgeDriver(bridge, inter, ip)
} else if bridgeDriver == "linux_bridge" {
} else if bridgeDriver == DRV_LINUX_BRIDGE {
return NewLinuxBridgeDeriver(bridge, inter, ip)
}
return nil, fmt.Errorf("Dirver %s not found", bridgeDriver)
}
func Prepare(bridgeDriver string) error {
if bridgeDriver == "openvswitch" {
if bridgeDriver == DRV_OPEN_VSWITCH {
return OVSPrepare()
} else if bridgeDriver == "linux_bridge" {
} else if bridgeDriver == DRV_LINUX_BRIDGE {
return LinuxBridgePrepare()
}
return fmt.Errorf("Dirver %s not found", bridgeDriver)
}
func CleanDeletedPorts(bridgeDriver string) {
if bridgeDriver == "openvswitch" {
if bridgeDriver == DRV_OPEN_VSWITCH {
cleanOvsBridge()
} else if bridgeDriver == "linux_bridge" {
} else if bridgeDriver == DRV_LINUX_BRIDGE {
cleanLinuxBridge()
}
}
+20 -92
View File
@@ -22,10 +22,10 @@ import (
"yunion.io/x/log"
"yunion.io/x/pkg/utils"
"yunion.io/x/onecloud/pkg/apis/compute"
"yunion.io/x/onecloud/pkg/hostman/options"
"yunion.io/x/onecloud/pkg/hostman/system_service"
"yunion.io/x/onecloud/pkg/util/bwutils"
"yunion.io/x/onecloud/pkg/util/netutils2"
"yunion.io/x/onecloud/pkg/util/ovsutils"
"yunion.io/x/onecloud/pkg/util/procutils"
)
@@ -117,19 +117,26 @@ func (o *SOVSBridgeDriver) GenerateIfupScripts(scriptPath string, nic jsonutils.
func (o *SOVSBridgeDriver) getUpScripts(nic jsonutils.JSONObject) (string, error) {
var (
bridge, _ = nic.GetString("bridge")
ifname, _ = nic.GetString("ifname")
ip, _ = nic.GetString("ip")
mac, _ = nic.GetString("mac")
vlan, _ = nic.Int("vlan")
bridge, _ = nic.GetString("bridge")
ifname, _ = nic.GetString("ifname")
ip, _ = nic.GetString("ip")
mac, _ = nic.GetString("mac")
netId, _ = nic.GetString("net_id")
vlan, _ = nic.Int("vlan")
vpcProvider, _ = nic.GetString("vpc", "provider")
)
if vpcProvider == compute.VPC_PROVIDER_OVN {
bridge = options.HostOptions.OvnIntegrationBridge
}
s := "#!/bin/bash\n\n"
s += fmt.Sprintf("SWITCH='%s'\n", bridge)
s += fmt.Sprintf("IF='%s'\n", ifname)
s += fmt.Sprintf("IP='%s'\n", ip)
s += fmt.Sprintf("MAC='%s'\n", mac)
s += fmt.Sprintf("VLAN_ID=%d\n", vlan)
s += fmt.Sprintf("NET_ID=%s\n", netId)
limit, burst, err := bwutils.GetOvsBwValues(nic)
if err != nil {
return "", err
@@ -155,15 +162,15 @@ func (o *SOVSBridgeDriver) getUpScripts(nic jsonutils.JSONObject) (string, error
s += " TAG=\"tag=$VLAN_ID\"\n"
s += "fi\n"
s += "ovs-vsctl add-port $SWITCH $IF $TAG\n"
if vpcProvider == compute.VPC_PROVIDER_OVN {
s += "ovs-vsctl set Interface $IF external_ids:iface-id=iface-$NET_ID-$IF\n"
}
s += "PORT=$(ovs-ofctl show $SWITCH | grep -w $IF)\n"
s += "PORT=$(echo $PORT | awk 'BEGIN{FS=\"(\"}{print $1}')\n"
s += "OFCTL=$(ovs-vsctl get-controller $SWITCH)\n"
s += "if [ -z \"$OFCTL\" ]; then\n"
s += " ovs-vsctl set Interface $IF ingress_policing_rate=$LIMIT\n"
s += " ovs-vsctl set Interface $IF ingress_policing_burst=$BURST\n"
for _, r := range o.GetOfRules(nic) {
s += " " + o.AddFlow(r.cond, r.priority, r.actions)
}
s += "fi\n"
s += "if [ $LIMIT_DOWNLOAD != \"0mbit\" ]; then\n"
s += " tc qdisc del dev $IF root 2>/dev/null\n"
@@ -197,11 +204,6 @@ func (o *SOVSBridgeDriver) getDownScripts(nic jsonutils.JSONObject) (string, err
s += "fi\n"
s += "OFCTL=$(ovs-vsctl get-controller $SWITCH)\n"
s += "PORT=$(echo $PORT | awk 'BEGIN{FS=\"(\"}{print $1}')\n"
s += "if [ -z \"$OFCTL\" ]; then\n"
for _, r := range o.GetOfRules(nic) {
s += " " + o.DelFlow(r.cond)
}
s += "fi\n"
s += "ip link set dev $IF down\n"
s += "ovs-vsctl -- --if-exists del-port $SWITCH $IF\n"
return s, nil
@@ -213,85 +215,7 @@ type SRule struct {
actions string
}
func (o *SOVSBridgeDriver) AddFlow(cond string, priority int, actions string) string {
s := ""
s += fmt.Sprintf("ovs-ofctl add-flow $SWITCH \"%s", cond)
s += fmt.Sprintf(" priority=%d", priority)
s += fmt.Sprintf(" actions=%s\"\n", actions)
return s
}
func (o *SOVSBridgeDriver) DoAddFlow(cond string, pri int, actions, swt string) error {
return procutils.NewCommand("ovs-ofctl", "add-flow", swt,
fmt.Sprintf("%s priority=%d actions=%s", cond, pri, actions)).Run()
}
func (o *SOVSBridgeDriver) DelFlow(cond string) string {
return fmt.Sprintf("ovs-ofctl del-flows $SWITCH \"%s\"\n", cond)
}
func (o *SOVSBridgeDriver) GetOfRules(nic jsonutils.JSONObject) []SRule {
rules := []SRule{}
metadataPort := o.GetMetadataServerPort()
rules = append(rules,
SRule{9000, fmt.Sprintf("table=0 in_port=local tcp nw_dst=$IP tp_src=%d", metadataPort),
"mod_nw_src=169.254.169.254,mod_tp_src:80,output:$PORT"},
SRule{9500, "table=0 in_port=$PORT udp tp_src=68 tp_dst=67", "local"},
SRule{8000, "table=0 in_port=$PORT", "resubmit(,1)"},
)
if vlan, _ := nic.Int("vlan"); vlan != 1 {
rules = append(rules,
SRule{4901, "table=1 dl_dst=$MAC,dl_vlan=$VLAN_ID", "strip_vlan,output:$PORT"})
}
rules = append(rules,
SRule{4900, "table=1 dl_dst=$MAC", "output:$PORT"})
return rules
}
func (o *SOVSBridgeDriver) RegisterHostlocalServer(mac, ip string) error {
if !options.HostOptions.EnableOpenflowController {
metadataPort := o.GetMetadataServerPort()
if err := o.DoAddFlow("table=0 ipv6", 20000, "drop", o.bridge.String()); err != nil {
log.Errorln(err)
return err
}
if err := o.DoAddFlow("table=0 tcp nw_dst=169.254.169.254 tp_dst=80", 10000,
fmt.Sprintf("mod_dl_dst:%s,mod_nw_dst:%s,mod_tp_dst:%d,local",
mac, ip, metadataPort),
o.bridge.String()); err != nil {
log.Errorln(err)
return err
}
log.Infof("OVS: metadata server %s:%d", ip, metadataPort)
k8sCidr := options.HostOptions.K8sClusterCidr
if len(k8sCidr) > 0 {
addr, mask, err := netutils2.PrefixSplit(k8sCidr)
if err != nil {
log.Errorln(err)
return err
}
k8sCidr = fmt.Sprintf("%s/%d", addr, mask)
log.Infof("OVS: Kubernetes cluster IP range: %s", k8sCidr)
err = o.DoAddFlow(fmt.Sprintf("table=0 ip,nw_dst=%s", k8sCidr),
10050, fmt.Sprintf("mod_dl_dst:%s,local", mac), o.bridge.String())
if err != nil {
log.Errorln(err)
return err
}
}
err := o.DoAddFlow("table=0", 0, "resubmit(,1)", o.bridge.String())
if err != nil {
log.Errorln(err)
return err
}
err = o.DoAddFlow("table=1", 0, "normal", o.bridge.String())
if err != nil {
log.Errorln(err)
return err
}
}
return nil
}
@@ -351,3 +275,7 @@ func NewOVSBridgeDriver(bridge, inter, ip string) (*SOVSBridgeDriver, error) {
ovsDrv.drv = ovsDrv
return ovsDrv, nil
}
func NewOVSBridgeDriverByName(bridge string) (*SOVSBridgeDriver, error) {
return NewOVSBridgeDriver(bridge, "", "")
}
+38 -13
View File
@@ -93,6 +93,14 @@ func (h *SHostInfo) GetBridgeDev(bridge string) hostbridge.IBridgeDriver {
return n.BridgeDev
}
}
if bridge == options.HostOptions.OvnIntegrationBridge {
drv, err := hostbridge.NewOVSBridgeDriverByName(bridge)
if err != nil {
log.Errorf("create ovn bridge driver: %v", err)
return nil
}
return drv
}
return nil
}
@@ -133,6 +141,9 @@ func (h *SHostInfo) Init() error {
if err := h.parseConfig(); err != nil {
return err
}
if err := h.setupOvnChassis(); err != nil {
return err
}
log.Infof("Start detectHostInfo")
if err := h.detectHostInfo(); err != nil {
return err
@@ -140,6 +151,19 @@ func (h *SHostInfo) Init() error {
return nil
}
func (h *SHostInfo) setupOvnChassis() error {
opts := &options.HostOptions
if opts.BridgeDriver != hostbridge.DRV_OPEN_VSWITCH {
return nil
}
log.Infof("Start setting up ovn chassis")
oh := NewOvnHelper(h)
if err := oh.Init(); err != nil {
return err
}
return nil
}
func (h *SHostInfo) generateLocalNetworkConfig() (string, error) {
netIp, dev, err := netutils2.DefaultSrcIpDev()
if err != nil {
@@ -327,11 +351,11 @@ func (h *SHostInfo) detectHostInfo() error {
h.detectKvmModuleSupport()
h.detectNestSupport()
if err := h.detectiveSyssoftwareInfo(); err != nil {
if err := h.detectSyssoftwareInfo(); err != nil {
return err
}
h.detectiveStorageSystem()
h.detectStorageSystem()
if options.HostOptions.CheckSystemServices {
if err := h.checkSystemServices(); err != nil {
@@ -353,7 +377,7 @@ func (h *SHostInfo) checkSystemServices() error {
return nil
}
func (h *SHostInfo) detectiveStorageSystem() {
func (h *SHostInfo) detectStorageSystem() {
var stype = api.DISK_TYPE_ROTATE
if options.HostOptions.DiskIsSsd {
stype = api.DISK_TYPE_SSD
@@ -483,7 +507,7 @@ func (h *SHostInfo) detectNestSupport() {
}
}
func (h *SHostInfo) detectiveOsDist() {
func (h *SHostInfo) detectOsDist() {
files, err := procutils.NewRemoteCommandAsFarAsPossible("sh", "-c", "ls /etc/*elease").Output()
if err != nil {
log.Errorln(err)
@@ -503,13 +527,13 @@ func (h *SHostInfo) detectiveOsDist() {
break
}
}
log.Infof("DetectiveOsDist %s %s", h.sysinfo.OsDistribution, h.sysinfo.OsVersion)
log.Infof("DetectOsDist %s %s", h.sysinfo.OsDistribution, h.sysinfo.OsVersion)
if len(h.sysinfo.OsDistribution) == 0 {
log.Errorln("Failed to detect distribution info")
}
}
func (h *SHostInfo) detectiveKernelVersion() {
func (h *SHostInfo) detectKernelVersion() {
out, err := procutils.NewCommand("uname", "-r").Output()
if err != nil {
log.Errorln(err)
@@ -517,17 +541,17 @@ func (h *SHostInfo) detectiveKernelVersion() {
h.sysinfo.KernelVersion = strings.TrimSpace(string(out))
}
func (h *SHostInfo) detectiveSyssoftwareInfo() error {
h.detectiveOsDist()
h.detectiveKernelVersion()
if err := h.detectiveQemuVersion(); err != nil {
func (h *SHostInfo) detectSyssoftwareInfo() error {
h.detectOsDist()
h.detectKernelVersion()
if err := h.detectQemuVersion(); err != nil {
return err
}
h.detectiveOvsVersion()
h.detectOvsVersion()
return nil
}
func (h *SHostInfo) detectiveQemuVersion() error {
func (h *SHostInfo) detectQemuVersion() error {
cmd := qemutils.GetQemu(options.HostOptions.DefaultQemuVersion)
version, err := procutils.NewRemoteCommandAsFarAsPossible(cmd, "--version").Output()
if err != nil {
@@ -547,7 +571,7 @@ func (h *SHostInfo) detectiveQemuVersion() error {
return nil
}
func (h *SHostInfo) detectiveOvsVersion() {
func (h *SHostInfo) detectOvsVersion() {
version, err := procutils.NewCommand("ovs-vsctl", "--version").Output()
if err != nil {
log.Errorln(err)
@@ -846,6 +870,7 @@ func (h *SHostInfo) updateHostRecord(hostId string) {
}
content.Set("__meta__", jsonutils.Marshal(h.getSysInfo()))
content.Set("version", jsonutils.NewString(version.GetShortString()))
content.Set("ovn_version", jsonutils.NewString(MustGetOvnVersion()))
var (
res jsonutils.JSONObject
+159
View File
@@ -0,0 +1,159 @@
// 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 hostinfo
import (
"fmt"
"yunion.io/x/pkg/errors"
"yunion.io/x/onecloud/pkg/hostman/options"
"yunion.io/x/onecloud/pkg/hostman/system_service"
"yunion.io/x/onecloud/pkg/util/netutils2"
"yunion.io/x/onecloud/pkg/util/procutils"
)
const (
ErrOvnService = errors.Error("ovn controller")
ErrOvnConfig = errors.Error("ovn controller configuration")
)
type OvnHelper struct {
hi *SHostInfo
}
func NewOvnHelper(hi *SHostInfo) *OvnHelper {
oh := &OvnHelper{
hi: hi,
}
return oh
}
func (oh *OvnHelper) Init() (err error) {
defer func() {
if panicVal := recover(); panicVal != nil {
err = panicVal.(error)
}
}()
oh.mustPrepOvsdbConfig()
oh.mustPrepService()
return nil
}
func (oh *OvnHelper) mustPrepOvsdbConfig() {
var (
args = []string{"set", "Open_vSwitch", "."}
opts = &options.HostOptions
)
{
if opts.OvnIntegrationBridge == "" {
panic(errors.Wrap(ErrOvnConfig, "bad config: ovn_integration_bridge"))
}
args = append(args, fmt.Sprintf("external_ids:ovn-bridge=%s",
opts.OvnIntegrationBridge))
}
{
encapIp := opts.OvnEncapIp
if encapIp == "" {
var err error
encapIp, err = netutils2.MyIP()
if err != nil {
panic(errors.Wrap(ErrOvnConfig, "determine default encap ip"))
}
}
args = append(args, "external_ids:ovn-encap-type=geneve")
args = append(args, fmt.Sprintf("external_ids:ovn-encap-ip=%s", encapIp))
}
{
if opts.OvnSouthDatabase == "" {
panic(errors.Wrap(ErrOvnConfig, "bad config: ovn_south_database"))
}
args = append(args, fmt.Sprintf("external_ids:ovn-remote=%s",
opts.OvnSouthDatabase))
}
output, err := procutils.NewCommand("ovs-vsctl", args...).Output()
if err != nil {
panic(errors.Wrapf(err, "configuring ovn-controller: %s", string(output)))
}
}
func (oh *OvnHelper) mustPrepService() {
ovn := system_service.GetService("ovn-controller")
if !ovn.IsInstalled() {
panic(errors.Wrap(ErrOvnService, "not installed"))
}
if ovn.IsEnabled() {
// - ovn-controller Requires "openvswitch.service"
// - openvswitch service should be disabled on startup
if err := ovn.Disable(); err != nil {
panic(errors.Wrap(err, "disable ovn-controller on startup"))
}
}
if err := ovn.Start(false); err != nil {
panic(errors.Wrap(err, "start ovn-controller"))
}
}
func MustGetOvnVersion() string {
output, err := procutils.NewCommand("ovn-controller", "--version").Output()
if err != nil {
return ""
}
return ovnExtractVersion(string(output))
}
func ovnExtractVersion(in string) string {
r := make([]rune, 0, 8)
var (
dot = false
ndot = 0
digit = 0
)
reset := func() {
dot = false
ndot = 0
digit = 0
}
for _, c := range in {
switch {
case c == '.':
if dot || digit == 0 {
reset()
continue
}
r = append(r, c)
dot = true
ndot += 1
digit = 0
case c >= '0' && c <= '9':
dot = false
if digit < 3 {
r = append(r, c)
digit += 1
continue
}
reset()
default:
if ndot > 0 && ndot < 3 {
return string(r)
}
reset()
}
}
if ndot > 0 && ndot < 3 {
return string(r)
}
return ""
}
+67
View File
@@ -0,0 +1,67 @@
// 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 hostinfo
import (
"testing"
)
func TestMustGetOvnVersion(t *testing.T) {
cases := []struct {
in string
out string
}{
{
in: `
ovn-controller (Open vSwitch) 2.9
OpenFlow versions 0x4:0x4
`,
out: "2.9",
},
{
in: `
ovn-controller (Open vSwitch) 2.9.6
OpenFlow versions 0x4:0x4
`,
out: "2.9.6",
},
{
in: `
ovn-controller (Open vSwitch) 2.9.100
OpenFlow versions 0x4:0x4
`,
out: "2.9.100",
},
{
in: `
ovn-controller (Open vSwitch) 2.9.1000
OpenFlow versions 0x4:0x4
`,
out: "",
},
{
in: `
ovn-controller (Open vSwitch) 2.9.6.1
`,
out: "",
},
}
for _, c := range cases {
got := ovnExtractVersion(c.in)
if got != c.out {
t.Fatalf("got: %s, want: %s, input:\n%s", got, c.out, c.in)
}
}
}
+4
View File
@@ -110,6 +110,10 @@ type SHostOptions struct {
EnableRemoteExecutor bool `help:"Enable remote executor" default:"false"`
ExecutorSocketPath string `help:"Executor socket path" default:"/var/run/exec.sock"`
CommonConfigFile string `help:"common config file for container"`
OvnSouthDatabase string `help:"address for accessing ovn south database" default:"unix:/var/run/openvswitch/ovnsb_db.sock"`
OvnIntegrationBridge string `help:"name of integration bridge for logical ports" default:"brvpc"`
OvnEncapIp string `help:"encap ip for ovn datapath. Default to output src address of default route"`
}
var HostOptions SHostOptions
@@ -0,0 +1,33 @@
// 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 system_service
type SOvnController struct {
*SBaseSystemService
}
func NewOvnControllerService() *SOvnController {
return &SOvnController{
NewBaseSystemService("ovn-controller", nil),
}
}
func (s *SOvnController) Reload(kwargs map[string]interface{}) error {
return s.reload(s.GetConfig(kwargs), s.GetConfigFile())
}
func (s *SOvnController) BgReload(kwargs map[string]interface{}) {
go s.reload(s.GetConfig(kwargs), s.GetConfigFile())
}
+10 -9
View File
@@ -44,15 +44,16 @@ var serviceMap map[string]ISystemService
func Init() {
serviceMap = map[string]ISystemService{
"ntpd": NewNtpdService(),
"telegraf": NewTelegrafService(),
"host_sdnagent": NewHostSdnagentService(),
"openvswitch": NewOpenvswitchService(),
"fluentbit": NewFluentbitService(),
"kube_agent": NewKubeAgentService(),
"lxcfs": NewLxcfsService(),
"docker": NewDockerService(),
"host-deployer": NewHostDeployerService(),
"ntpd": NewNtpdService(),
"telegraf": NewTelegrafService(),
"host_sdnagent": NewHostSdnagentService(),
"openvswitch": NewOpenvswitchService(),
"ovn-controller": NewOvnControllerService(),
"fluentbit": NewFluentbitService(),
"kube_agent": NewKubeAgentService(),
"lxcfs": NewLxcfsService(),
"docker": NewDockerService(),
"host-deployer": NewHostDeployerService(),
}
}
+9 -6
View File
@@ -54,7 +54,6 @@ type StandaloneResource struct {
Id string
Name string
ExternalId string
Description string
IsEmulated bool
}
@@ -69,7 +68,7 @@ type StatusStandaloneResource struct {
Status string
}
type EnabledStatusStandaloneResourceBase struct {
type EnabledStatusStandaloneResource struct {
StatusStandaloneResource
Enabled bool
@@ -84,10 +83,6 @@ type VirtualResource struct {
PendingDeleted bool
}
type ManagedResource struct {
ManagerId string
}
func (r *VirtualResource) GetPendingDeleted() bool {
return r.PendingDeleted
}
@@ -97,3 +92,11 @@ type SharableVirtualResource struct {
IsPublic bool
}
type ManagedResource struct {
ManagerId string
}
type ExternalizedResource struct {
ExternalId string
}
+1 -1
View File
@@ -21,7 +21,7 @@ import (
)
type Host struct {
EnabledStatusStandaloneResourceBase
EnabledStatusStandaloneResource
Rack string
Slots string
+11 -11
View File
@@ -26,7 +26,7 @@ import (
"yunion.io/x/onecloud/pkg/mcclient"
)
type BaseManagerInterface interface {
type IBaseManager interface {
Version() string
GetApiVersion() string
GetKeyword() string
@@ -44,7 +44,7 @@ type ManagerContext struct {
}
type Manager interface {
BaseManagerInterface
IBaseManager
/* resource list
GET <base_url>/<resource_plural_keyword>
e.g GET <base_url>/alarms
@@ -143,7 +143,7 @@ type Manager interface {
}
type JointManager interface {
BaseManagerInterface
IBaseManager
MasterManager() Manager
SlaveManager() Manager
Get(s *mcclient.ClientSession, mid, sid string, params jsonutils.JSONObject) (jsonutils.JSONObject, error)
@@ -163,7 +163,7 @@ type JointManager interface {
}
var (
modules map[string]map[string][]BaseManagerInterface
modules map[string]map[string][]IBaseManager
jointModules map[string]map[string][]JointManager
)
@@ -171,7 +171,7 @@ func _getJointKey(mod1 Manager, mod2 Manager) string {
return fmt.Sprintf("%s-%s", mod1.KeyString(), mod2.KeyString())
}
func ensureModuleNotRegistered(mod, newMod BaseManagerInterface) {
func ensureModuleNotRegistered(mod, newMod IBaseManager) {
modSvcType := mod.ServiceType()
newModSvcType := newMod.ServiceType()
if mod == newMod {
@@ -182,18 +182,18 @@ func ensureModuleNotRegistered(mod, newMod BaseManagerInterface) {
}
}
func Register(version string, mod BaseManagerInterface) {
func Register(version string, mod IBaseManager) {
if modules == nil {
modules = make(map[string]map[string][]BaseManagerInterface)
modules = make(map[string]map[string][]IBaseManager)
}
modtable, ok := modules[version]
if !ok {
modtable = make(map[string][]BaseManagerInterface)
modtable = make(map[string][]IBaseManager)
modules[version] = modtable
}
mods, ok := modtable[mod.KeyString()]
if !ok {
mods = make([]BaseManagerInterface, 0)
mods = make([]IBaseManager, 0)
}
for i := range mods {
ensureModuleNotRegistered(mods[i], mod)
@@ -203,7 +203,7 @@ func Register(version string, mod BaseManagerInterface) {
// modtable[mod.KeyString()] = append(mods, mod)
}
func RegisterJointModule(version string, mod BaseManagerInterface) {
func RegisterJointModule(version string, mod IBaseManager) {
jointMod, ok := mod.(JointManager)
if ok { // also a joint manager
jointKey := _getJointKey(jointMod.MasterManager(), jointMod.SlaveManager())
@@ -240,7 +240,7 @@ func registerAllJointModules() {
}
}
func _getModule(session *mcclient.ClientSession, name string) (BaseManagerInterface, error) {
func _getModule(session *mcclient.ClientSession, name string) (IBaseManager, error) {
modtable, ok := modules[session.GetApiVersion()]
if !ok {
return nil, fmt.Errorf("No such version: %s", session.GetApiVersion())
+1 -1
View File
@@ -25,7 +25,7 @@ func NewCloudnetManager(keyword, keywordPlural string, columns, adminColumns []s
}
var (
registerV2 = func(mod modulebase.BaseManagerInterface) {
registerV2 = func(mod modulebase.IBaseManager) {
modulebase.Register("v2", mod)
}
)
+6 -6
View File
@@ -19,28 +19,28 @@ import (
"yunion.io/x/onecloud/pkg/mcclient/modulebase"
)
func registerCompute(mod modulebase.BaseManagerInterface) {
func registerCompute(mod modulebase.IBaseManager) {
registerComputeV1(mod)
registerComputeV2(mod)
}
func registerComputeV1(mod modulebase.BaseManagerInterface) {
func registerComputeV1(mod modulebase.IBaseManager) {
modulebase.Register("v1", mod)
}
func registerComputeV2(mod modulebase.BaseManagerInterface) {
func registerComputeV2(mod modulebase.IBaseManager) {
mod.SetApiVersion(mcclient.V2_API_VERSION)
modulebase.Register("v2", mod)
}
func register(mod modulebase.BaseManagerInterface) {
func register(mod modulebase.IBaseManager) {
modulebase.Register("v1", mod)
}
func registerV2(mod modulebase.BaseManagerInterface) {
func registerV2(mod modulebase.IBaseManager) {
modulebase.Register("v2", mod)
}
func Register(mod modulebase.BaseManagerInterface) {
func Register(mod modulebase.IBaseManager) {
register(mod)
}
+6 -2
View File
@@ -210,8 +210,9 @@ type BaseListOptions struct {
Scope string `help:"resource scope" choices:"system|domain|project|user"`
System *bool `help:"Show system resource"`
PendingDelete *bool `help:"Show only pending deleted resource"`
PendingDeleteAll *bool `help:"Show all resources including pending deleted" json:"-"`
PendingDelete *bool `help:"Show only pending deleted resources"`
PendingDeleteAll *bool `help:"Show also pending-deleted resources" json:"-"`
DeleteAll *bool `help:"Show also deleted resources" json:"-"`
ShowEmulated *bool `help:"Show all resources including the emulated resources"`
ExportFile string `help:"Export to file" metavar:"<EXPORT_FILE_PATH>" json:"-"`
@@ -259,6 +260,9 @@ func (opts *BaseListOptions) Params() (*jsonutils.JSONDict, error) {
if len(opts.Filter) == 0 {
params.Remove("filter_any")
}
if BoolV(opts.DeleteAll) {
params.Set("delete", jsonutils.NewString("all"))
}
if BoolV(opts.PendingDeleteAll) {
params.Set("pending_delete", jsonutils.NewString("all"))
params.Set("details", jsonutils.JSONTrue) // required to get pending_deleted field
@@ -60,7 +60,16 @@ func (p *NetworkPredicate) Execute(u *core.Unit, c core.Candidater) (bool, []cor
h := NewPredicateHelper(p, u, c)
getter := c.Getter()
ovnCapable := getter.OvnCapable()
networks := getter.Networks()
ovnNetworks := []*api.CandidateNetwork{}
for i := len(networks) - 1; i >= 0; i -= 1 {
net := networks[i]
if net.Provider == computeapi.CLOUD_PROVIDER_ONECLOUD {
networks = append(networks[:i], networks[i+1:]...)
ovnNetworks = append(ovnNetworks, net)
}
}
d := u.SchedData()
@@ -210,21 +219,7 @@ func (p *NetworkPredicate) Execute(u *core.Unit, c core.Candidater) (bool, []cor
}
isNetworkAvaliable := func(n *computeapi.NetworkConfig, counters *core.MinCounters, networks []*api.CandidateNetwork) []core.PredicateFailureReason {
if len(networks) == 0 {
return []core.PredicateFailureReason{
FailReason{Reason: ErrNoAvailableNetwork},
}
}
if n.Network == "" {
counters0 := core.NewCounters()
retMsg := isRandomNetworkAvailable(n.Address, n.Domain, n.Private, n.Exit, n.Wire, counters0)
counters.Add(counters0)
return retMsg
}
errMsgs := make([]core.PredicateFailureReason, 0)
for _, net := range networks {
if !(n.Network == net.GetId() || n.Network == net.GetName()) {
errMsgs = append(errMsgs, &FailReason{
@@ -258,9 +253,34 @@ func (p *NetworkPredicate) Execute(u *core.Unit, c core.Candidater) (bool, []cor
var errMsgs []core.PredicateFailureReason
for _, n := range d.Networks {
if errMsg := isNetworkAvaliable(n, counters, networks); len(errMsg) != 0 {
errMsgs = append(errMsgs, errMsg...)
if len(networks) == 0 && len(ovnNetworks) == 0 {
errMsgs = append(errMsgs, FailReason{
Reason: ErrNoAvailableNetwork,
})
continue
}
if n.Network == "" {
counters0 := core.NewCounters()
retMsg := isRandomNetworkAvailable(n.Address, n.Domain, n.Private, n.Exit, n.Wire, counters0)
counters.Add(counters0)
errMsgs = append(errMsgs, retMsg...)
continue
}
var availCheckErrs []core.PredicateFailureReason
if errMsg := isNetworkAvaliable(n, counters, networks); len(errMsg) == 0 {
continue
} else {
availCheckErrs = append(availCheckErrs, errMsg...)
}
if ovnCapable {
if errMsg := isNetworkAvaliable(n, counters, ovnNetworks); len(errMsg) == 0 {
continue
} else {
availCheckErrs = append(availCheckErrs, errMsg...)
}
}
errMsgs = append(errMsgs, availCheckErrs...)
}
if len(errMsgs) > 0 {
@@ -115,6 +115,12 @@ func (p *NetworkSchedtagPredicate) IsResourceFitInput(u *core.Unit, c core.Candi
}
if net.Network == "" {
if network.Provider == computeapi.CLOUD_PROVIDER_ONECLOUD {
return &FailReason{
Reason: fmt.Sprintf("Network %s is from onecloud vpc %s", network.Name, network.VpcId),
Type: NetworkTypeMatch,
}
}
netTypes := p.GetNetworkTypes(net.NetType)
if !utils.IsInStringArray(network.ServerType, netTypes) {
return &FailReason{
+3
View File
@@ -101,6 +101,9 @@ type CandidateStorage struct {
type CandidateNetwork struct {
*models.SNetwork
Schedtags []models.SSchedtag `json:"schedtags"`
Provider string
VpcId string
}
type CandidateGroup struct {
+47
View File
@@ -19,6 +19,7 @@ import (
"yunion.io/x/jsonutils"
"yunion.io/x/log"
"yunion.io/x/pkg/errors"
"yunion.io/x/pkg/utils"
"yunion.io/x/sqlchemy"
@@ -136,6 +137,10 @@ func (b baseHostGetter) Networks() []*api.CandidateNetwork {
return b.h.Networks
}
func (b baseHostGetter) OvnCapable() bool {
return false
}
func (b baseHostGetter) ResourceType() string {
return reviseResourceType(b.h.ResourceType)
}
@@ -222,6 +227,9 @@ func newBaseHostDesc(host *computemodels.SHost) (*BaseHostDesc, error) {
if err := desc.fillNetworks(host); err != nil {
return nil, fmt.Errorf("Fill networks error: %v", err)
}
if err := desc.fillOnecloudVpcNetworks(); err != nil {
return nil, fmt.Errorf("Fill onecloud vpc networks error: %v", err)
}
if err := desc.fillZone(host); err != nil {
return nil, fmt.Errorf("Fill zone error: %v", err)
@@ -371,6 +379,45 @@ func (b *BaseHostDesc) fillNetworks(host *computemodels.SHost) error {
return nil
}
func (b *BaseHostDesc) fillOnecloudVpcNetworks() error {
nets := computemodels.NetworkManager.Query()
wires := computemodels.WireManager.Query().SubQuery()
vpcs := computemodels.VpcManager.Query().SubQuery()
regions := computemodels.CloudregionManager.Query().SubQuery()
q := nets.AppendField(nets.QueryFields()...)
q = q.AppendField(
vpcs.Field("id", "vpc_id"),
regions.Field("provider"),
)
q = q.Join(wires, sqlchemy.Equals(wires.Field("id"), nets.Field("wire_id")))
q = q.Join(vpcs, sqlchemy.Equals(vpcs.Field("id"), wires.Field("vpc_id")))
q = q.Join(regions, sqlchemy.Equals(regions.Field("id"), vpcs.Field("cloudregion_id")))
q = q.Filter(sqlchemy.AND(
sqlchemy.Equals(regions.Field("provider"), computeapi.CLOUD_PROVIDER_ONECLOUD),
sqlchemy.NOT(sqlchemy.Equals(vpcs.Field("id"), computeapi.DEFAULT_VPC_ID)),
))
type Row struct {
computemodels.SNetwork
VpcId string
Provider string
}
rows := []Row{}
if err := q.All(&rows); err != nil {
return errors.Wrap(err, "query onecloud vpc networks")
}
for i := range rows {
row := &rows[i]
candidateNet := &api.CandidateNetwork{
SNetwork: &row.SNetwork,
VpcId: row.VpcId,
Provider: row.Provider,
}
b.Networks = append(b.Networks, candidateNet)
}
return nil
}
func (b *BaseHostDesc) fillStorages(host *computemodels.SHost) error {
ss := make([]*api.CandidateStorage, 0)
for _, s := range host.GetHoststorages() {
+4
View File
@@ -93,6 +93,10 @@ func (h *hostGetter) GetFreePort(netId string) int {
return h.h.GetFreePort(netId)
}
func (h *hostGetter) OvnCapable() bool {
return len(h.h.OvnVersion) > 0
}
type HostDesc struct {
*BaseHostDesc
+2 -8
View File
@@ -180,14 +180,8 @@ func (c *MinCounters) Add(counter Counter) {
}
func (c *MinCounters) GetCount() int64 {
if len(c.counters) == 0 {
return EmptyCapacity
}
minCount := c.counters[0].GetCount()
if len(c.counters) == 1 {
return minCount
}
for _, c0 := range c.counters[1:] {
minCount := EmptyCapacity
for _, c0 := range c.counters {
count := c0.GetCount()
if count < minCount {
minCount = count
+10 -17
View File
@@ -405,8 +405,8 @@ completed:
func findCandidatesThatFit(unit *Unit, candidates []Candidater, predicates map[string]FitPredicate) ([]Candidater, error) {
var filtered []Candidater
ok, err, newPredicates := preExecPredicate(unit, candidates, predicates)
if !ok {
newPredicates, err := preExecPredicate(unit, candidates, predicates)
if err != nil {
return nil, err
}
@@ -457,27 +457,20 @@ func findCandidatesThatFit(unit *Unit, candidates []Candidater, predicates map[s
return filtered, nil
}
func preExecPredicate(unit *Unit, candidates []Candidater, predicates map[string]FitPredicate) (bool, error, map[string]FitPredicate) {
var (
name string
predicate FitPredicate
ok bool
err error
newPredicateFuncs map[string]FitPredicate
)
newPredicateFuncs = make(map[string]FitPredicate)
for name, predicate = range predicates {
func preExecPredicate(unit *Unit, candidates []Candidater, predicates map[string]FitPredicate) (map[string]FitPredicate, error) {
newPredicateFuncs := map[string]FitPredicate{}
for name, predicate := range predicates {
// generate new FitPredicates because of race condition?
newPredicate := predicate.Clone()
ok, err = newPredicate.PreExecute(unit, candidates)
ok, err := newPredicate.PreExecute(unit, candidates)
if err != nil {
return nil, err
}
if ok {
newPredicateFuncs[name] = newPredicate
}
if err != nil {
return false, err, nil
}
}
return true, err, newPredicateFuncs
return newPredicateFuncs, nil
}
type WaitGroupWrapper struct {
+1
View File
@@ -66,6 +66,7 @@ type CandidatePropertyGetter interface {
HostSchedtags() []computemodels.SSchedtag
Storages() []*api.CandidateStorage
Networks() []*api.CandidateNetwork
OvnCapable() bool
Status() string
HostStatus() string
Enabled() bool
+2 -2
View File
@@ -47,7 +47,7 @@ var (
func GetKVMModuleSupport() string {
if len(kvmModuleSupport) == 0 {
kvmModuleSupport = detectiveKVMModuleSupport()
kvmModuleSupport = detectKVMModuleSupport()
}
return kvmModuleSupport
}
@@ -76,7 +76,7 @@ func IsProcessorAmd() bool {
return false
}
func detectiveKVMModuleSupport() string {
func detectKVMModuleSupport() string {
var km = KVM_MODULE_UNSUPPORT
if ModprobeKvmModule(KVM_MODULE_INTEL, false, false) {
km = KVM_MODULE_INTEL
+129
View File
@@ -0,0 +1,129 @@
// 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 apihelper
import (
"context"
"sync"
"time"
"yunion.io/x/log"
"yunion.io/x/pkg/errors"
"yunion.io/x/onecloud/pkg/mcclient"
"yunion.io/x/onecloud/pkg/mcclient/auth"
"yunion.io/x/onecloud/pkg/vpcagent/options"
)
const (
ErrSync = errors.Error("sync error")
)
type APIHelper struct {
opts *options.Options
modelSets IModelSets
modelSetsCh chan IModelSets
mcclientSession *mcclient.ClientSession
}
func NewAPIHelper(opts *options.Options, modelSets IModelSets) (*APIHelper, error) {
modelSetsCh := make(chan IModelSets)
helper := &APIHelper{
opts: opts,
modelSets: modelSets,
modelSetsCh: modelSetsCh,
}
return helper, nil
}
func (h *APIHelper) Start(ctx context.Context) {
defer func() {
log.Infoln("apihelper: bye")
wg := ctx.Value("wg").(*sync.WaitGroup)
wg.Done()
}()
h.run(ctx)
tickDuration := time.Duration(h.opts.APISyncInterval) * time.Second
tick := time.NewTimer(tickDuration)
defer tick.Stop()
for {
select {
case <-tick.C:
h.run(ctx)
tick.Reset(tickDuration)
case <-ctx.Done():
return
}
}
}
func (h *APIHelper) ModelSets() <-chan IModelSets {
return h.modelSetsCh
}
func (h *APIHelper) run(ctx context.Context) {
changed, err := h.doSync(ctx)
if err != nil {
log.Errorln(err)
}
if changed {
mssCopy := h.modelSets.Copy()
select {
case h.modelSetsCh <- mssCopy:
case <-ctx.Done():
}
}
}
func (h *APIHelper) doSync(ctx context.Context) (changed bool, err error) {
{
stime := time.Now()
defer func() {
elapsed := time.Since(stime)
log.Infof("sync data done, changed: %v, elapsed: %s", changed, elapsed.String())
}()
}
s := h.adminClientSession(ctx)
r, err := SyncModelSets(h.modelSets, s, h.opts.APIListBatchSize)
if err != nil {
return false, err
}
if !r.Correct {
return false, errors.Wrap(ErrSync, "incorrect")
}
changed = r.Changed
return changed, nil
}
func (h *APIHelper) adminClientSession(ctx context.Context) *mcclient.ClientSession {
s := h.mcclientSession
if s != nil {
token := s.GetToken()
expires := token.GetExpires()
if time.Now().Add(time.Hour).After(expires) {
return s
}
}
region := h.opts.CommonOptions.Region
apiVersion := "v2"
h.mcclientSession = auth.GetAdminSession(ctx, region, apiVersion)
return h.mcclientSession
}
+15
View File
@@ -0,0 +1,15 @@
// 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 apihelper // import "yunion.io/x/onecloud/pkg/vpcagent/apihelper"
+60
View File
@@ -0,0 +1,60 @@
// 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 apihelper
import (
"yunion.io/x/onecloud/pkg/cloudcommon/db"
mcclient "yunion.io/x/onecloud/pkg/mcclient"
mcclient_modulebase "yunion.io/x/onecloud/pkg/mcclient/modulebase"
)
type ModelSetsUpdateResult struct {
Correct bool
Changed bool
}
type IModelSets interface {
NewEmpty() IModelSets
ModelSetList() []IModelSet
ApplyUpdates(IModelSets) ModelSetsUpdateResult
Copy() IModelSets
}
type IModelSet interface {
ModelManager() mcclient_modulebase.IBaseManager
NewModel() db.IModel
AddModel(db.IModel)
Copy() IModelSet
}
func SyncModelSets(mssOld IModelSets, s *mcclient.ClientSession, batchSize int) (r ModelSetsUpdateResult, err error) {
mss := mssOld.ModelSetList()
mssNews := mssOld.NewEmpty()
for i, msNew := range mssNews.ModelSetList() {
minUpdatedAt := ModelSetMaxUpdatedAt(mss[i])
err = GetModels(&GetModelsOptions{
ClientSession: s,
ModelManager: msNew.ModelManager(),
MinUpdatedAt: minUpdatedAt,
ModelSet: msNew,
BatchListSize: batchSize,
})
if err != nil {
return
}
}
r = mssOld.ApplyUpdates(mssNews)
return r, nil
}
+264
View File
@@ -0,0 +1,264 @@
// 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 apihelper
import (
"fmt"
"reflect"
"time"
"yunion.io/x/jsonutils"
"yunion.io/x/log"
"yunion.io/x/pkg/util/timeutils"
"yunion.io/x/onecloud/pkg/cloudcommon/db"
mcclient "yunion.io/x/onecloud/pkg/mcclient"
mcclient_modulebase "yunion.io/x/onecloud/pkg/mcclient/modulebase"
"yunion.io/x/onecloud/pkg/mcclient/options"
)
// A hack to workaround the IsZero() in timeutils.Utcify. This depends on the
// fact that database time has a resolution of 1-second
var PseudoZeroTime = time.Time{}.Add(time.Nanosecond)
type GetModelsOptions struct {
ClientSession *mcclient.ClientSession
ModelManager mcclient_modulebase.IBaseManager
ModelSet IModelSet
BatchListSize int
MinUpdatedAt time.Time
}
func GetModels(opts *GetModelsOptions) error {
man := opts.ModelManager
manKeyPlural := man.KeyString()
minUpdatedAt := opts.MinUpdatedAt
minUpdatedAtFilter := func(time time.Time) string {
// TODO add GE
tstr := timeutils.MysqlTime(time)
return fmt.Sprintf("updated_at.ge('%s')", tstr)
}
setNextListParams := func(params *jsonutils.JSONDict, lastUpdatedAt time.Time, lastResult *mcclient_modulebase.ListResult) (time.Time, error) {
// NOTE: the updated_at field has second-level resolution.
// If they all have the same date...
var max time.Time
nmax := 0
n := len(lastResult.Data)
// find out the max updated_at date in the result set, and how
// many in the set has this date
for i := n - 1; i >= 0; i-- {
j := lastResult.Data[i]
updatedAt, err := j.GetTime("updated_at")
if err != nil {
log.Warningf("%s: updated_at field: %s, %s",
manKeyPlural, err, j.String())
continue
}
if max.IsZero() {
max = updatedAt
}
if max.Equal(updatedAt) {
nmax += 1
}
}
// error if we do not have valid date
if max.IsZero() {
return time.Time{}, fmt.Errorf("%s: cannot find next updated_at after '%q'",
manKeyPlural, lastUpdatedAt)
}
var newTime time.Time
var newOffset int
// if not all updated_at date are the same, then we can
// continue to the next age.
if nmax < n || (!max.Equal(lastUpdatedAt) && !max.Equal(PseudoZeroTime)) {
newTime = max
newOffset = nmax
} else {
newTime = lastUpdatedAt
newOffset = lastResult.Offset + n
}
params.Set("filter.0", jsonutils.NewString(minUpdatedAtFilter(newTime)))
params.Set("offset", jsonutils.NewInt(int64(newOffset)))
return newTime, nil
}
listOptions := options.BaseListOptions{
Admin: options.Bool(true),
Details: options.Bool(true),
Filter: []string{
minUpdatedAtFilter(minUpdatedAt), // order matters, filter.0
"manager_id.isnullorempty()", // len(manager_id) > 0 is for pubcloud objects
"external_id.isnullorempty()", // len(external_id) > 0 is for pubcloud objects
},
OrderBy: []string{"updated_at"},
Order: "asc",
Limit: options.Int(opts.BatchListSize),
Offset: options.Int(0),
}
if !minUpdatedAt.Equal(PseudoZeroTime) {
// Only fetching pending deletes when we are doing incremental fetch
listOptions.PendingDeleteAll = options.Bool(true)
listOptions.DeleteAll = options.Bool(true)
}
params, err := listOptions.Params()
if err != nil {
return fmt.Errorf("%s: making list params: %s", manKeyPlural, err)
}
//XXX
//params.Set(api.LBAGENT_QUERY_ORIG_KEY, jsonutils.NewString(api.LBAGENT_QUERY_ORIG_VAL))
entriesJson := []jsonutils.JSONObject{}
for {
var err error
listResult, err := opts.ModelManager.List(opts.ClientSession, params)
if err != nil {
return fmt.Errorf("%s: list failed with updated_at.gt('%s'): %s",
manKeyPlural, minUpdatedAt, err)
}
entriesJson = append(entriesJson, listResult.Data...)
if listResult.Offset+len(listResult.Data) >= listResult.Total {
break
}
minUpdatedAt, err = setNextListParams(params, minUpdatedAt, listResult)
if err != nil {
return fmt.Errorf("%s: %s", manKeyPlural, err)
}
}
{
err := InitializeModelSetFromJSON(opts.ModelSet, entriesJson)
if err != nil {
return fmt.Errorf("%s: initializing model set failed: %s",
manKeyPlural, err)
}
}
return nil
}
func InitializeModelSetFromJSON(set IModelSet, entriesJson []jsonutils.JSONObject) error {
setRv := reflect.ValueOf(set)
for _, kRv := range setRv.MapKeys() {
zRv := reflect.Value{}
setRv.SetMapIndex(kRv, zRv)
}
manKeyPlural := set.ModelManager().KeyString()
for _, entryJson := range entriesJson {
m := set.NewModel()
var err error
err = entryJson.Unmarshal(m)
if err != nil {
return fmt.Errorf("%s: unmarshal: %v: %s", manKeyPlural, err, entryJson.String())
}
{
keyRv := reflect.ValueOf(m.GetId())
oldMRv := setRv.MapIndex(keyRv)
if oldMRv.IsValid() {
// check version
oldM := oldMRv.Interface().(db.IModel)
oldVersion := oldM.GetUpdateVersion()
version := m.GetUpdateVersion()
if oldVersion > version {
oldUpdatedAt := oldM.GetUpdatedAt()
updatedAt := m.GetUpdatedAt()
log.Warningf("prefer loadbalancer with update_version %d(%s) to %d(%s)",
oldVersion, oldUpdatedAt, version, updatedAt)
return nil
}
}
}
set.AddModel(m)
}
return nil
}
func ModelSetMaxUpdatedAt(set IModelSet) time.Time {
r := PseudoZeroTime
setRv := reflect.ValueOf(set)
for _, kRv := range setRv.MapKeys() {
mRv := setRv.MapIndex(kRv)
m := mRv.Interface().(db.IModel)
updatedAt := m.GetUpdatedAt()
if r.Before(updatedAt) {
r = updatedAt
}
}
return r
}
type ModelSetUpdateResult struct {
Changed bool
MaxUpdatedAt time.Time
}
// ModelSetApplyUpdates applies bSet to aSet.
//
// - PendingDeleted in bSet are removed from aSet
// - Newer models in bSet are updated in aSet
func ModelSetApplyUpdates(aSet, bSet IModelSet) *ModelSetUpdateResult {
r := &ModelSetUpdateResult{
Changed: false,
}
{
a := ModelSetMaxUpdatedAt(aSet)
b := ModelSetMaxUpdatedAt(bSet)
if b.After(a) {
r.MaxUpdatedAt = b
} else {
r.MaxUpdatedAt = a
}
}
aSetRv := reflect.ValueOf(aSet)
bSetRv := reflect.ValueOf(bSet)
for _, kRv := range bSetRv.MapKeys() {
bMRv := bSetRv.MapIndex(kRv)
b := bMRv.Interface()
bM := b.(db.IModel)
bGone := bM.GetDeleted()
if !bGone {
bVM, ok := b.(db.IPendingDeletable)
if ok {
bGone = bVM.GetPendingDeleted()
}
}
aMRv := aSetRv.MapIndex(kRv)
if aMRv.IsValid() {
aM := aMRv.Interface().(db.IModel)
if bGone {
// oops, deleted
aSetRv.SetMapIndex(kRv, reflect.Value{})
r.Changed = true
continue
}
if aM.GetUpdateVersion() < bM.GetUpdateVersion() {
// oops, updated
aSetRv.SetMapIndex(kRv, bMRv)
r.Changed = true
continue
}
} else {
if bGone {
// hmm, gone before even knowning
continue
}
// oops, new member
aSetRv.SetMapIndex(kRv, bMRv)
r.Changed = true
}
}
return r
}
+15
View File
@@ -0,0 +1,15 @@
// 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 "yunion.io/x/onecloud/pkg/lbagent/models"
+59
View File
@@ -0,0 +1,59 @@
// 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 (
compute_models "yunion.io/x/onecloud/pkg/compute/models"
)
type Vpc struct {
compute_models.SVpc
Networks Networks `json:"-"`
}
func (el *Vpc) Copy() *Vpc {
return &Vpc{
SVpc: el.SVpc,
}
}
type Network struct {
compute_models.SNetwork
// returned as extra column
VpcId string
Vpc *Vpc `json:"-"`
Guestnetworks Guestnetworks `json:"-"`
}
func (el *Network) Copy() *Network {
return &Network{
SNetwork: el.SNetwork,
VpcId: el.VpcId,
}
}
type Guestnetwork struct {
compute_models.SGuestnetwork
Network *Network `json:"-"`
}
func (el *Guestnetwork) Copy() *Guestnetwork {
return &Guestnetwork{
SGuestnetwork: el.SGuestnetwork,
}
}
+147
View File
@@ -0,0 +1,147 @@
// 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 (
"yunion.io/x/log"
"yunion.io/x/onecloud/pkg/apis/compute"
"yunion.io/x/onecloud/pkg/cloudcommon/db"
mcclient_modulebase "yunion.io/x/onecloud/pkg/mcclient/modulebase"
mcclient_modules "yunion.io/x/onecloud/pkg/mcclient/modules"
"yunion.io/x/onecloud/pkg/vpcagent/apihelper"
)
type Vpcs map[string]*Vpc
type Networks map[string]*Network
type Guestnetworks map[string]*Guestnetwork // guestId as key
func (set Vpcs) ModelManager() mcclient_modulebase.IBaseManager {
return &mcclient_modules.Vpcs
}
func (set Vpcs) NewModel() db.IModel {
return &Vpc{}
}
func (set Vpcs) AddModel(i db.IModel) {
m := i.(*Vpc)
if m.Id == compute.DEFAULT_VPC_ID {
return
}
set[m.Id] = m
}
func (set Vpcs) Copy() apihelper.IModelSet {
setCopy := Vpcs{}
for id, el := range set {
setCopy[id] = el.Copy()
}
return setCopy
}
func (ms Vpcs) joinNetworks(subEntries Networks) bool {
for _, m := range ms {
m.Networks = Networks{}
}
correct := true
for subId, subEntry := range subEntries {
id := subEntry.VpcId
if id == compute.DEFAULT_VPC_ID {
continue
}
m, ok := ms[id]
if !ok {
log.Warningf("network %s(%s): vpc id %s not found",
subEntry.Name, subEntry.Id, id)
correct = false
continue
}
if _, ok := m.Networks[subId]; ok {
log.Warningf("network %s(%s): already joined",
subEntry.Name, subEntry.Id)
continue
}
subEntry.Vpc = m
m.Networks[subId] = subEntry
}
return correct
}
func (set Networks) ModelManager() mcclient_modulebase.IBaseManager {
return &mcclient_modules.Networks
}
func (set Networks) NewModel() db.IModel {
return &Network{}
}
func (set Networks) AddModel(i db.IModel) {
m := i.(*Network)
set[m.Id] = m
}
func (set Networks) Copy() apihelper.IModelSet {
setCopy := Networks{}
for id, el := range set {
setCopy[id] = el.Copy()
}
return setCopy
}
func (ms Networks) joinGuestnetworks(subEntries Guestnetworks) bool {
for _, m := range ms {
m.Guestnetworks = Guestnetworks{}
}
correct := true
for _, subEntry := range subEntries {
id := subEntry.NetworkId
m, ok := ms[id]
if !ok {
log.Warningf("network id %s not found", id)
correct = false
continue
}
subId := subEntry.GuestId
if _, ok := m.Guestnetworks[subId]; ok {
log.Warningf("guestnetwork id %s/%s already joined", id, subId)
continue
}
subEntry.Network = m
m.Guestnetworks[subId] = subEntry
}
return correct
}
func (set Guestnetworks) ModelManager() mcclient_modulebase.IBaseManager {
return &mcclient_modules.Servernetworks
}
func (set Guestnetworks) NewModel() db.IModel {
return &Guestnetwork{}
}
func (set Guestnetworks) AddModel(i db.IModel) {
m := i.(*Guestnetwork)
set[m.GuestId] = m
}
func (set Guestnetworks) Copy() apihelper.IModelSet {
setCopy := Guestnetworks{}
for id, el := range set {
setCopy[id] = el.Copy()
}
return setCopy
}
+115
View File
@@ -0,0 +1,115 @@
// 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 (
"strings"
"time"
"yunion.io/x/onecloud/pkg/vpcagent/apihelper"
)
// pluralMap maps from KeyPlurals to underscore-separated field names
var pluralMap = map[string]string{}
func init() {
// XXX drop this
ss := []string{
"vpcs",
"networks",
"guestnetworks",
}
for _, s := range ss {
k := strings.Replace(s, "_", "", -1)
pluralMap[k] = s
}
}
type ModelSetsMaxUpdatedAt struct {
Vpcs time.Time
Networks time.Time
Guestnetworks time.Time
}
func NewModelSetsMaxUpdatedAt() *ModelSetsMaxUpdatedAt {
return &ModelSetsMaxUpdatedAt{
Vpcs: apihelper.PseudoZeroTime,
Networks: apihelper.PseudoZeroTime,
Guestnetworks: apihelper.PseudoZeroTime,
}
}
type ModelSets struct {
Vpcs Vpcs
Networks Networks
Guestnetworks Guestnetworks
}
func NewModelSets() *ModelSets {
return &ModelSets{
Vpcs: Vpcs{},
Networks: Networks{},
Guestnetworks: Guestnetworks{},
}
}
func (mss *ModelSets) ModelSetList() []apihelper.IModelSet {
// it's ordered this way to favour creation, not deletion
return []apihelper.IModelSet{
mss.Vpcs,
mss.Networks,
mss.Guestnetworks,
}
}
func (mss *ModelSets) NewEmpty() apihelper.IModelSets {
return NewModelSets()
}
func (mss *ModelSets) Copy() apihelper.IModelSets {
mssCopy := &ModelSets{
Vpcs: mss.Vpcs.Copy().(Vpcs),
Networks: mss.Networks.Copy().(Networks),
Guestnetworks: mss.Guestnetworks.Copy().(Guestnetworks),
}
mssCopy.join()
return mssCopy
}
func (mss *ModelSets) ApplyUpdates(mssNews apihelper.IModelSets) apihelper.ModelSetsUpdateResult {
r := apihelper.ModelSetsUpdateResult{
Changed: false,
Correct: true,
}
mssList := mss.ModelSetList()
mssNewsList := mssNews.ModelSetList()
for i, mss := range mssList {
mssNews := mssNewsList[i]
msR := apihelper.ModelSetApplyUpdates(mss, mssNews)
if !r.Changed && msR.Changed {
r.Changed = true
}
}
if r.Changed {
r.Correct = mss.join()
}
return r
}
func (mss *ModelSets) join() bool {
correct0 := mss.Vpcs.joinNetworks(mss.Networks)
correct1 := mss.Networks.joinGuestnetworks(mss.Guestnetworks)
return correct0 && correct1
}
+15
View File
@@ -0,0 +1,15 @@
// 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 options // import "yunion.io/x/onecloud/pkg/vpcagent/options"
+68
View File
@@ -0,0 +1,68 @@
// 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 options
import (
"yunion.io/x/pkg/errors"
"yunion.io/x/onecloud/pkg/apis/compute"
common_options "yunion.io/x/onecloud/pkg/cloudcommon/options"
)
const (
VPC_PROVIDER_OVN = "ovn"
)
const (
ErrInvalidVpcProvider = errors.Error("invalid vpc provider")
)
type VpcAgentOptions struct {
VpcProvider string `default:"ovn"`
APISyncInterval int `default:"10"`
APIListBatchSize int `default:"1024"`
OvnWorkerCheckInterval int `default:"180"`
OvnNorthDatabase string `help:"address for accessing ovn north database. Default to local unix socket"`
}
type Options struct {
common_options.CommonOptions
VpcAgentOptions
}
func (opts *Options) ValidateThenInit() error {
switch opts.VpcProvider {
case compute.VPC_PROVIDER_OVN:
case "":
return errors.Wrap(ErrInvalidVpcProvider, "empty")
default:
return errors.Wrapf(ErrInvalidVpcProvider, "unknown provider: %s", opts.VpcProvider)
}
if opts.APIListBatchSize <= 20 {
opts.APIListBatchSize = 20
}
if opts.APISyncInterval <= 10 {
opts.APISyncInterval = 10
}
if opts.OvnWorkerCheckInterval <= 60 {
opts.OvnWorkerCheckInterval = 60
}
return nil
}
+15
View File
@@ -0,0 +1,15 @@
// 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 ovn // import "yunion.io/x/onecloud/pkg/vpcagent/ovn"
+277
View File
@@ -0,0 +1,277 @@
// 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 ovn
import (
"context"
"crypto/md5"
"fmt"
"strings"
"yunion.io/x/pkg/errors"
agentmodels "yunion.io/x/onecloud/pkg/vpcagent/models"
"yunion.io/x/onecloud/pkg/vpcagent/ovnutil"
)
const (
externalKeyOcVersion = "oc-version"
externalKeyOcRef = "oc-ref"
)
type OVNNorthboundKeeper struct {
DB ovnutil.OVNNorthbound
cli *ovnutil.OvnNbCtl
}
func DumpOVNNorthbound(ctx context.Context, cli *ovnutil.OvnNbCtl) (*OVNNorthboundKeeper, error) {
db := ovnutil.OVNNorthbound{}
itbls := []ovnutil.ITable{
&db.LogicalSwitch,
&db.LogicalSwitchPort,
&db.LogicalRouter,
&db.LogicalRouterPort,
&db.DHCPOptions,
}
args := []string{"--format=json", "list", "<tbl>"}
for _, itbl := range itbls {
tbl := itbl.OvnTableName()
args[2] = tbl
res := cli.Must(ctx, "List "+tbl, args)
if err := ovnutil.UnmarshalJSON([]byte(res.Output), itbl); err != nil {
return nil, errors.Wrapf(err, "Unmarshal %s:\n%s",
itbl.OvnTableName(), res.Output)
}
}
keeper := &OVNNorthboundKeeper{
DB: db,
cli: cli,
}
return keeper, nil
}
func ovnCreateArgs(irow ovnutil.IRow, idRef string) []string {
args := append([]string{
"--", "--id=@" + idRef, "create", irow.OvnTableName(),
}, irow.OvnArgs()...)
return args
}
func (keeper *OVNNorthboundKeeper) ClaimVpc(ctx context.Context, vpc *agentmodels.Vpc) error {
var (
args []string
ocVersion = fmt.Sprintf("%s.%d", vpc.UpdatedAt, vpc.UpdateVersion)
)
lrvpc := &ovnutil.LogicalRouter{
Name: fmt.Sprintf("vpc-lr-%s", vpc.Id),
}
if m := keeper.DB.LogicalRouter.FindOneMatchNonZeros(lrvpc); m != nil {
m.OvnSetExternalIds(externalKeyOcVersion, ocVersion)
return nil
}
args = append(args, ovnCreateArgs(lrvpc, "lrvpc")...)
return keeper.cli.Must(ctx, "ClaimVpc", args)
}
func hashMac(in ...string) string {
h := md5.New()
for _, s := range in {
h.Write([]byte(s))
}
sum := h.Sum(nil)
b := sum[0]
b &= 0xfe
b |= 0x02
mac := fmt.Sprintf("%02x", b)
for _, b := range sum[1:6] {
mac += fmt.Sprintf(":%02x", b)
}
return mac
}
func (keeper *OVNNorthboundKeeper) ClaimNetwork(ctx context.Context, network *agentmodels.Network) error {
var (
lsnetName = fmt.Sprintf("subnet-ls-%s", network.Id)
lrnetpName = fmt.Sprintf("subnet-lrp-%s", network.Id)
lsnetpName = fmt.Sprintf("subnet-lsp-%s", network.Id)
lsnetmpName = fmt.Sprintf("subnet-lsmp-%s", network.Id)
lrvpcName = fmt.Sprintf("vpc-lr-%s", network.VpcId)
rpMac = hashMac(network.Id, "rp")
dhcpMac = hashMac(network.Id, "dhcp")
mdMac = hashMac(network.Id, "md")
mdIp = "169.254.169.254"
)
lsnet := &ovnutil.LogicalSwitch{
Name: lsnetName,
}
lrnetp := &ovnutil.LogicalRouterPort{
Name: lrnetpName,
Mac: rpMac,
Networks: []string{fmt.Sprintf("%s/%d", network.GuestGateway, network.GuestIpMask)},
}
lsnetp := &ovnutil.LogicalSwitchPort{
Name: lsnetpName,
Type: "router",
Addresses: []string{"router"},
Options: map[string]string{
"router-port": lrnetpName,
},
}
lsnetmp := &ovnutil.LogicalSwitchPort{
Name: lsnetmpName,
Type: "localport",
Addresses: []string{fmt.Sprintf("%s %s", mdMac, mdIp)},
}
dhcpopts := &ovnutil.DHCPOptions{
Cidr: fmt.Sprintf("%s/%d", network.GuestIpStart, network.GuestIpMask),
Options: map[string]string{
"server_id": network.GuestGateway,
"server_mac": dhcpMac,
"lease_time": fmt.Sprintf("%d", 86400),
"router": network.GuestGateway,
"classless_static_route": fmt.Sprintf("{%s/32,0.0.0.0}", mdIp),
},
ExternalIds: map[string]string{
externalKeyOcRef: network.Id,
},
}
var (
args []string
ocVersion = fmt.Sprintf("%s.%d", network.UpdatedAt, network.UpdateVersion)
)
irows := []ovnutil.IRow{
lsnet,
lrnetp,
lsnetp,
lsnetmp,
dhcpopts,
}
{
irowsFound := make([]ovnutil.IRow, 0, len(irows))
for _, irow := range irows {
irowFound := keeper.DB.FindOneMatchNonZeros(irow)
if irowFound != nil {
irowsFound = append(irowsFound, irowFound)
}
}
// mark them anyway even if not all found, to avoid the destroy
// call at sweep stage
for _, irowFound := range irowsFound {
irowFound.OvnSetExternalIds(externalKeyOcVersion, ocVersion)
}
if len(irowsFound) == len(irows) {
return nil
}
args := ovnutil.OvnNbctlArgsDestroy(irowsFound)
if len(args) > 0 {
keeper.cli.Must(ctx, "ClaimNetwork cleanup", args)
}
}
args = append(args, ovnCreateArgs(lsnet, "lsnet")...)
args = append(args, ovnCreateArgs(lrnetp, "lrnetp")...)
args = append(args, ovnCreateArgs(lsnetp, "lsnetp")...)
args = append(args, ovnCreateArgs(lsnetmp, "lsnetmp")...)
args = append(args, ovnCreateArgs(dhcpopts, "dhcpopts")...)
args = append(args, "--", "add", "Logical_Switch", lsnetName, "ports", "@lsnetp", "@lsnetmp")
args = append(args, "--", "add", "Logical_Router", lrvpcName, "ports", "@lrnetp")
return keeper.cli.Must(ctx, "ClaimNetwork", args)
}
func (keeper *OVNNorthboundKeeper) ClaimGuestnetwork(ctx context.Context, guestnetwork *agentmodels.Guestnetwork) error {
var (
lsName = fmt.Sprintf("subnet-ls-%s", guestnetwork.NetworkId)
lspName = fmt.Sprintf("iface-%s-%s", guestnetwork.NetworkId, guestnetwork.Ifname)
ocVersion = fmt.Sprintf("%s.%d", guestnetwork.UpdatedAt, guestnetwork.UpdateVersion)
dhcpOpt string
)
{
dhcpOptQuery := &ovnutil.DHCPOptions{
ExternalIds: map[string]string{
externalKeyOcRef: guestnetwork.NetworkId,
},
}
if m := keeper.DB.DHCPOptions.FindOneMatchNonZeros(dhcpOptQuery); m != nil {
dhcpOpt = m.OvnUuid()
} else {
args := []string{
"--bare", "--columns=_uuid", "find", "DHCP_Options",
fmt.Sprintf("external_ids:%s=%q", externalKeyOcRef, guestnetwork.NetworkId),
}
res := keeper.cli.Must(ctx, "find dhcpopt", args)
dhcpOpt = strings.TrimSpace(res.Output)
}
}
lsp := &ovnutil.LogicalSwitchPort{
Name: lspName,
Addresses: []string{fmt.Sprintf("%s %s", guestnetwork.MacAddr, guestnetwork.IpAddr)},
PortSecurity: []string{fmt.Sprintf("%s %s/%d", guestnetwork.MacAddr, guestnetwork.IpAddr, guestnetwork.Network.GuestIpMask)},
Dhcpv4Options: &dhcpOpt,
}
if m := keeper.DB.LogicalSwitchPort.FindOneMatchNonZeros(lsp); m != nil {
m.OvnSetExternalIds(externalKeyOcVersion, ocVersion)
return nil
}
var args []string
args = append(args, ovnCreateArgs(lsp, "lsp")...)
args = append(args, "--", "add", "Logical_Switch", lsName, "ports", "@lsp")
return keeper.cli.Must(ctx, "ClaimGuestnetwork", args)
}
func (keeper *OVNNorthboundKeeper) Mark(ctx context.Context) {
db := &keeper.DB
itbls := []ovnutil.ITable{
&db.LogicalSwitch,
&db.LogicalSwitchPort,
&db.LogicalRouter,
&db.LogicalRouterPort,
&db.DHCPOptions,
}
for _, itbl := range itbls {
for _, irow := range itbl.Rows() {
irow.OvnRemoveExternalIds(externalKeyOcVersion)
}
}
}
func (keeper *OVNNorthboundKeeper) Sweep(ctx context.Context) error {
db := &keeper.DB
// isRoot=false tables at the end
itbls := []ovnutil.ITable{
&db.LogicalSwitchPort,
&db.LogicalRouterPort,
&db.LogicalSwitch,
&db.LogicalRouter,
&db.DHCPOptions,
}
var irows []ovnutil.IRow
for _, itbl := range itbls {
for _, irow := range itbl.Rows() {
_, ok := irow.OvnGetExternalIds(externalKeyOcVersion)
if !ok {
irows = append(irows, irow)
}
}
}
args := ovnutil.OvnNbctlArgsDestroy(irows)
if len(args) > 0 {
return keeper.cli.Must(ctx, "Sweep", args)
}
return nil
}
+27
View File
@@ -0,0 +1,27 @@
// 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 ovn
import (
"yunion.io/x/onecloud/pkg/apis/compute"
"yunion.io/x/onecloud/pkg/vpcagent/worker"
)
func init() {
worker.RegisterNewWorkerFunc(
compute.VPC_PROVIDER_OVN,
NewWorker,
)
}
+121
View File
@@ -0,0 +1,121 @@
// 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 ovn
import (
"context"
"runtime"
"runtime/debug"
"sync"
"time"
"yunion.io/x/log"
"yunion.io/x/pkg/errors"
"yunion.io/x/onecloud/pkg/vpcagent/apihelper"
agentmodels "yunion.io/x/onecloud/pkg/vpcagent/models"
"yunion.io/x/onecloud/pkg/vpcagent/options"
"yunion.io/x/onecloud/pkg/vpcagent/ovnutil"
"yunion.io/x/onecloud/pkg/vpcagent/worker"
)
type Worker struct {
opts *options.Options
apih *apihelper.APIHelper
}
func NewWorker(opts *options.Options) worker.IWorker {
modelSets := agentmodels.NewModelSets()
apih, err := apihelper.NewAPIHelper(opts, modelSets)
if err != nil {
return nil
}
w := &Worker{
opts: opts,
apih: apih,
}
return w
}
func (w *Worker) Start(ctx context.Context) {
wg := ctx.Value("wg").(*sync.WaitGroup)
defer func() {
log.Infoln("ovn: worker bye")
wg.Done()
}()
wg.Add(1)
go w.apih.Start(ctx)
tickDuration := time.Duration(w.opts.OvnWorkerCheckInterval) * time.Second
tick := time.NewTimer(tickDuration)
defer tick.Stop()
var mss *agentmodels.ModelSets
for {
select {
case imss := <-w.apih.ModelSets():
log.Infof("ovn: got new data from api helper")
mss = imss.(*agentmodels.ModelSets)
if err := w.run(ctx, mss); err != nil {
log.Errorf("ovn: %v", err)
}
case <-tick.C:
if mss != nil {
log.Infof("ovn: tick check")
if err := w.run(ctx, mss); err != nil {
log.Errorf("ovn: %v", err)
}
}
tick.Reset(tickDuration)
case <-ctx.Done():
return
}
}
}
func (w *Worker) run(ctx context.Context, mss *agentmodels.ModelSets) (err error) {
defer func() {
if panicVal := recover(); panicVal != nil {
if panicErr, ok := panicVal.(runtime.Error); ok {
err = errors.Wrap(panicErr, string(debug.Stack()))
} else if panicErr, ok := panicVal.(error); ok {
err = panicErr
} else {
panic(panicVal)
}
}
}()
ovnnbctl := ovnutil.NewOvnNbCtl(w.opts.OvnNorthDatabase)
ovndb, err := DumpOVNNorthbound(ctx, ovnnbctl)
if err != nil {
return err
}
ovndb.Mark(ctx)
for _, vpc := range mss.Vpcs {
ovndb.ClaimVpc(ctx, vpc)
for _, network := range vpc.Networks {
ovndb.ClaimNetwork(ctx, network)
for _, guestnetwork := range network.Guestnetworks {
ovndb.ClaimGuestnetwork(ctx, guestnetwork)
}
}
}
ovndb.Sweep(ctx)
return nil
}
+774
View File
@@ -0,0 +1,774 @@
// 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 ovnutil
import (
"yunion.io/x/pkg/errors"
)
const (
ErrBadType = errors.Error("bad type")
)
func panicErr(msg string) {
panic(errors.Wrap(ErrBadType, msg))
}
func panicErrf(fmtStr string, s ...interface{}) {
panic(errors.Wrapf(ErrBadType, fmtStr, s...))
}
func ensureTypedPair(val interface{}) (string, interface{}) {
arr, ok := val.([]interface{})
if !ok {
panicErr("ensureTypedPair: not an array")
}
if len(arr) != 2 {
panicErrf("ensureTypedPair: length is %d, want 2", len(arr))
}
typ, ok := arr[0].(string)
if !ok {
panicErr("ensureTypedPair: type not a string")
}
return typ, arr[1]
}
func ensureTyped(val interface{}, typ string) interface{} {
gotTyp, r := ensureTypedPair(val)
if gotTyp != typ {
panicErrf("ensureMultiples: got %s, want %s", gotTyp, typ)
}
return r
}
func ensureMultiples(val interface{}, typ string) []interface{} {
val = ensureTyped(val, typ)
mulVal, ok := val.([]interface{})
if !ok {
panicErr("ensureMultiples: val is not an array")
}
return mulVal
}
func probeEmpty(val interface{}) (r bool) {
defer func() {
recover()
}()
empty := ensureMultiples(val, "set")
if len(empty) != 0 {
return false
}
return true
}
func ensureUuid(val interface{}) string {
val = ensureTyped(val, "uuid")
r, ok := val.(string)
if !ok {
panicErr("bad uuid value")
}
return r
}
func ensureUuidMultiples(val interface{}) []string {
typ, val1 := ensureTypedPair(val)
if typ == "uuid" {
r, ok := val1.(string)
if !ok {
panicErr("uuid multiples: expect a string")
}
return []string{r}
}
if typ == "set" {
mulVal, ok := val1.([]interface{})
if !ok {
panicErr("uuid multiples: expect an array")
}
if len(mulVal) == 0 {
return nil
}
r := make([]string, len(mulVal))
for i, val := range mulVal {
r[i] = ensureUuid(val)
}
return r
}
panic("uuid multiple: unexpected type: " + typ)
}
func ensureBoolean(val interface{}) bool {
if r, ok := val.(bool); ok {
return r
}
panic(ErrBadType)
}
func ensureBooleanMultiples(val interface{}) []bool {
if ok := probeEmpty(val); ok {
return nil
}
if r, ok := val.(bool); ok {
return []bool{r}
}
mulVal := ensureMultiples(val, "set")
if len(mulVal) == 0 {
return nil
}
r := make([]bool, len(mulVal))
for i, val := range mulVal {
r[i] = ensureBoolean(val)
}
return r
}
func ensureBooleanOptional(val interface{}) *bool {
if ok := probeEmpty(val); ok {
return nil
}
r := ensureBoolean(val)
return &r
}
func ensureMapBooleanUuid(val interface{}) map[bool]string {
mulVal := ensureMultiples(val, "map")
if len(mulVal) == 0 {
return nil
}
r := map[bool]string{}
for _, pairVal := range mulVal {
pair, ok := pairVal.([]interface{})
if !ok {
panicErr("map: not an array")
}
if len(pair) != 2 {
panicErr("map: not a pair")
}
k := ensureBoolean(pair[0])
v := ensureUuid(pair[1])
r[k] = v
}
return r
}
func ensureMapBooleanString(val interface{}) map[bool]string {
mulVal := ensureMultiples(val, "map")
if len(mulVal) == 0 {
return nil
}
r := map[bool]string{}
for _, pairVal := range mulVal {
pair, ok := pairVal.([]interface{})
if !ok {
panicErr("map: not an array")
}
if len(pair) != 2 {
panicErr("map: not a pair")
}
k := ensureBoolean(pair[0])
v := ensureString(pair[1])
r[k] = v
}
return r
}
func ensureMapBooleanInteger(val interface{}) map[bool]int64 {
mulVal := ensureMultiples(val, "map")
if len(mulVal) == 0 {
return nil
}
r := map[bool]int64{}
for _, pairVal := range mulVal {
pair, ok := pairVal.([]interface{})
if !ok {
panicErr("map: not an array")
}
if len(pair) != 2 {
panicErr("map: not a pair")
}
k := ensureBoolean(pair[0])
v := ensureInteger(pair[1])
r[k] = v
}
return r
}
func ensureMapBooleanBoolean(val interface{}) map[bool]bool {
mulVal := ensureMultiples(val, "map")
if len(mulVal) == 0 {
return nil
}
r := map[bool]bool{}
for _, pairVal := range mulVal {
pair, ok := pairVal.([]interface{})
if !ok {
panicErr("map: not an array")
}
if len(pair) != 2 {
panicErr("map: not a pair")
}
k := ensureBoolean(pair[0])
v := ensureBoolean(pair[1])
r[k] = v
}
return r
}
func ensureMapBooleanReal(val interface{}) map[bool]float64 {
mulVal := ensureMultiples(val, "map")
if len(mulVal) == 0 {
return nil
}
r := map[bool]float64{}
for _, pairVal := range mulVal {
pair, ok := pairVal.([]interface{})
if !ok {
panicErr("map: not an array")
}
if len(pair) != 2 {
panicErr("map: not a pair")
}
k := ensureBoolean(pair[0])
v := ensureReal(pair[1])
r[k] = v
}
return r
}
func ensureReal(val interface{}) float64 {
if r, ok := val.(float64); ok {
return r
}
panic(ErrBadType)
}
func ensureRealMultiples(val interface{}) []float64 {
if ok := probeEmpty(val); ok {
return nil
}
if r, ok := val.(float64); ok {
return []float64{r}
}
mulVal := ensureMultiples(val, "set")
if len(mulVal) == 0 {
return nil
}
r := make([]float64, len(mulVal))
for i, val := range mulVal {
r[i] = ensureReal(val)
}
return r
}
func ensureRealOptional(val interface{}) *float64 {
if ok := probeEmpty(val); ok {
return nil
}
r := ensureReal(val)
return &r
}
func ensureMapRealString(val interface{}) map[float64]string {
mulVal := ensureMultiples(val, "map")
if len(mulVal) == 0 {
return nil
}
r := map[float64]string{}
for _, pairVal := range mulVal {
pair, ok := pairVal.([]interface{})
if !ok {
panicErr("map: not an array")
}
if len(pair) != 2 {
panicErr("map: not a pair")
}
k := ensureReal(pair[0])
v := ensureString(pair[1])
r[k] = v
}
return r
}
func ensureMapRealInteger(val interface{}) map[float64]int64 {
mulVal := ensureMultiples(val, "map")
if len(mulVal) == 0 {
return nil
}
r := map[float64]int64{}
for _, pairVal := range mulVal {
pair, ok := pairVal.([]interface{})
if !ok {
panicErr("map: not an array")
}
if len(pair) != 2 {
panicErr("map: not a pair")
}
k := ensureReal(pair[0])
v := ensureInteger(pair[1])
r[k] = v
}
return r
}
func ensureMapRealBoolean(val interface{}) map[float64]bool {
mulVal := ensureMultiples(val, "map")
if len(mulVal) == 0 {
return nil
}
r := map[float64]bool{}
for _, pairVal := range mulVal {
pair, ok := pairVal.([]interface{})
if !ok {
panicErr("map: not an array")
}
if len(pair) != 2 {
panicErr("map: not a pair")
}
k := ensureReal(pair[0])
v := ensureBoolean(pair[1])
r[k] = v
}
return r
}
func ensureMapRealReal(val interface{}) map[float64]float64 {
mulVal := ensureMultiples(val, "map")
if len(mulVal) == 0 {
return nil
}
r := map[float64]float64{}
for _, pairVal := range mulVal {
pair, ok := pairVal.([]interface{})
if !ok {
panicErr("map: not an array")
}
if len(pair) != 2 {
panicErr("map: not a pair")
}
k := ensureReal(pair[0])
v := ensureReal(pair[1])
r[k] = v
}
return r
}
func ensureMapRealUuid(val interface{}) map[float64]string {
mulVal := ensureMultiples(val, "map")
if len(mulVal) == 0 {
return nil
}
r := map[float64]string{}
for _, pairVal := range mulVal {
pair, ok := pairVal.([]interface{})
if !ok {
panicErr("map: not an array")
}
if len(pair) != 2 {
panicErr("map: not a pair")
}
k := ensureReal(pair[0])
v := ensureUuid(pair[1])
r[k] = v
}
return r
}
func ensureUuidOptional(val interface{}) *string {
if ok := probeEmpty(val); ok {
return nil
}
r := ensureUuid(val)
return &r
}
func ensureMapUuidReal(val interface{}) map[string]float64 {
mulVal := ensureMultiples(val, "map")
if len(mulVal) == 0 {
return nil
}
r := map[string]float64{}
for _, pairVal := range mulVal {
pair, ok := pairVal.([]interface{})
if !ok {
panicErr("map: not an array")
}
if len(pair) != 2 {
panicErr("map: not a pair")
}
k := ensureUuid(pair[0])
v := ensureReal(pair[1])
r[k] = v
}
return r
}
func ensureMapUuidUuid(val interface{}) map[string]string {
mulVal := ensureMultiples(val, "map")
if len(mulVal) == 0 {
return nil
}
r := map[string]string{}
for _, pairVal := range mulVal {
pair, ok := pairVal.([]interface{})
if !ok {
panicErr("map: not an array")
}
if len(pair) != 2 {
panicErr("map: not a pair")
}
k := ensureUuid(pair[0])
v := ensureUuid(pair[1])
r[k] = v
}
return r
}
func ensureMapUuidString(val interface{}) map[string]string {
mulVal := ensureMultiples(val, "map")
if len(mulVal) == 0 {
return nil
}
r := map[string]string{}
for _, pairVal := range mulVal {
pair, ok := pairVal.([]interface{})
if !ok {
panicErr("map: not an array")
}
if len(pair) != 2 {
panicErr("map: not a pair")
}
k := ensureUuid(pair[0])
v := ensureString(pair[1])
r[k] = v
}
return r
}
func ensureMapUuidInteger(val interface{}) map[string]int64 {
mulVal := ensureMultiples(val, "map")
if len(mulVal) == 0 {
return nil
}
r := map[string]int64{}
for _, pairVal := range mulVal {
pair, ok := pairVal.([]interface{})
if !ok {
panicErr("map: not an array")
}
if len(pair) != 2 {
panicErr("map: not a pair")
}
k := ensureUuid(pair[0])
v := ensureInteger(pair[1])
r[k] = v
}
return r
}
func ensureMapUuidBoolean(val interface{}) map[string]bool {
mulVal := ensureMultiples(val, "map")
if len(mulVal) == 0 {
return nil
}
r := map[string]bool{}
for _, pairVal := range mulVal {
pair, ok := pairVal.([]interface{})
if !ok {
panicErr("map: not an array")
}
if len(pair) != 2 {
panicErr("map: not a pair")
}
k := ensureUuid(pair[0])
v := ensureBoolean(pair[1])
r[k] = v
}
return r
}
func ensureString(val interface{}) string {
if r, ok := val.(string); ok {
return r
}
panic(ErrBadType)
}
func ensureStringMultiples(val interface{}) []string {
if ok := probeEmpty(val); ok {
return nil
}
if r, ok := val.(string); ok {
return []string{r}
}
mulVal := ensureMultiples(val, "set")
if len(mulVal) == 0 {
return nil
}
r := make([]string, len(mulVal))
for i, val := range mulVal {
r[i] = ensureString(val)
}
return r
}
func ensureStringOptional(val interface{}) *string {
if ok := probeEmpty(val); ok {
return nil
}
r := ensureString(val)
return &r
}
func ensureMapStringUuid(val interface{}) map[string]string {
mulVal := ensureMultiples(val, "map")
if len(mulVal) == 0 {
return nil
}
r := map[string]string{}
for _, pairVal := range mulVal {
pair, ok := pairVal.([]interface{})
if !ok {
panicErr("map: not an array")
}
if len(pair) != 2 {
panicErr("map: not a pair")
}
k := ensureString(pair[0])
v := ensureUuid(pair[1])
r[k] = v
}
return r
}
func ensureMapStringString(val interface{}) map[string]string {
mulVal := ensureMultiples(val, "map")
if len(mulVal) == 0 {
return nil
}
r := map[string]string{}
for _, pairVal := range mulVal {
pair, ok := pairVal.([]interface{})
if !ok {
panicErr("map: not an array")
}
if len(pair) != 2 {
panicErr("map: not a pair")
}
k := ensureString(pair[0])
v := ensureString(pair[1])
r[k] = v
}
return r
}
func ensureMapStringInteger(val interface{}) map[string]int64 {
mulVal := ensureMultiples(val, "map")
if len(mulVal) == 0 {
return nil
}
r := map[string]int64{}
for _, pairVal := range mulVal {
pair, ok := pairVal.([]interface{})
if !ok {
panicErr("map: not an array")
}
if len(pair) != 2 {
panicErr("map: not a pair")
}
k := ensureString(pair[0])
v := ensureInteger(pair[1])
r[k] = v
}
return r
}
func ensureMapStringBoolean(val interface{}) map[string]bool {
mulVal := ensureMultiples(val, "map")
if len(mulVal) == 0 {
return nil
}
r := map[string]bool{}
for _, pairVal := range mulVal {
pair, ok := pairVal.([]interface{})
if !ok {
panicErr("map: not an array")
}
if len(pair) != 2 {
panicErr("map: not a pair")
}
k := ensureString(pair[0])
v := ensureBoolean(pair[1])
r[k] = v
}
return r
}
func ensureMapStringReal(val interface{}) map[string]float64 {
mulVal := ensureMultiples(val, "map")
if len(mulVal) == 0 {
return nil
}
r := map[string]float64{}
for _, pairVal := range mulVal {
pair, ok := pairVal.([]interface{})
if !ok {
panicErr("map: not an array")
}
if len(pair) != 2 {
panicErr("map: not a pair")
}
k := ensureString(pair[0])
v := ensureReal(pair[1])
r[k] = v
}
return r
}
func ensureInteger(val interface{}) int64 {
if r, ok := val.(int64); ok {
return r
}
panic(ErrBadType)
}
func ensureIntegerMultiples(val interface{}) []int64 {
if ok := probeEmpty(val); ok {
return nil
}
if r, ok := val.(int64); ok {
return []int64{r}
}
mulVal := ensureMultiples(val, "set")
if len(mulVal) == 0 {
return nil
}
r := make([]int64, len(mulVal))
for i, val := range mulVal {
r[i] = ensureInteger(val)
}
return r
}
func ensureIntegerOptional(val interface{}) *int64 {
if ok := probeEmpty(val); ok {
return nil
}
r := ensureInteger(val)
return &r
}
func ensureMapIntegerReal(val interface{}) map[int64]float64 {
mulVal := ensureMultiples(val, "map")
if len(mulVal) == 0 {
return nil
}
r := map[int64]float64{}
for _, pairVal := range mulVal {
pair, ok := pairVal.([]interface{})
if !ok {
panicErr("map: not an array")
}
if len(pair) != 2 {
panicErr("map: not a pair")
}
k := ensureInteger(pair[0])
v := ensureReal(pair[1])
r[k] = v
}
return r
}
func ensureMapIntegerUuid(val interface{}) map[int64]string {
mulVal := ensureMultiples(val, "map")
if len(mulVal) == 0 {
return nil
}
r := map[int64]string{}
for _, pairVal := range mulVal {
pair, ok := pairVal.([]interface{})
if !ok {
panicErr("map: not an array")
}
if len(pair) != 2 {
panicErr("map: not a pair")
}
k := ensureInteger(pair[0])
v := ensureUuid(pair[1])
r[k] = v
}
return r
}
func ensureMapIntegerString(val interface{}) map[int64]string {
mulVal := ensureMultiples(val, "map")
if len(mulVal) == 0 {
return nil
}
r := map[int64]string{}
for _, pairVal := range mulVal {
pair, ok := pairVal.([]interface{})
if !ok {
panicErr("map: not an array")
}
if len(pair) != 2 {
panicErr("map: not a pair")
}
k := ensureInteger(pair[0])
v := ensureString(pair[1])
r[k] = v
}
return r
}
func ensureMapIntegerInteger(val interface{}) map[int64]int64 {
mulVal := ensureMultiples(val, "map")
if len(mulVal) == 0 {
return nil
}
r := map[int64]int64{}
for _, pairVal := range mulVal {
pair, ok := pairVal.([]interface{})
if !ok {
panicErr("map: not an array")
}
if len(pair) != 2 {
panicErr("map: not a pair")
}
k := ensureInteger(pair[0])
v := ensureInteger(pair[1])
r[k] = v
}
return r
}
func ensureMapIntegerBoolean(val interface{}) map[int64]bool {
mulVal := ensureMultiples(val, "map")
if len(mulVal) == 0 {
return nil
}
r := map[int64]bool{}
for _, pairVal := range mulVal {
pair, ok := pairVal.([]interface{})
if !ok {
panicErr("map: not an array")
}
if len(pair) != 2 {
panicErr("map: not a pair")
}
k := ensureInteger(pair[0])
v := ensureBoolean(pair[1])
r[k] = v
}
return r
}
+990
View File
@@ -0,0 +1,990 @@
// 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 ovnutil
func matchIntegerIfNonZero(a, b int64) bool {
var z int64
if b == z {
return true
}
return matchInteger(a, b)
}
func matchInteger(a, b int64) bool {
return a == b
}
func matchIntegerOptionalIfNonZero(a, b *int64) bool {
if b == nil {
return true
}
return matchIntegerOptional(a, b)
}
func matchIntegerOptional(a, b *int64) bool {
if a == nil && b == nil {
return true
} else if a != nil && b != nil {
return *a == *b
}
return false
}
func matchIntegerMultiplesIfNonZero(a, b []int64) bool {
if b == nil {
return true
}
return matchIntegerMultiples(a, b)
}
func matchIntegerMultiples(a, b []int64) bool {
if len(a) != len(b) {
return false
}
bCopy := make([]int64, len(b))
copy(bCopy, b)
for _, elA := range a {
for i := len(bCopy) - 1; i >= 0; i-- {
elB := bCopy[i]
if elA == elB {
bCopy = append(bCopy[:i], bCopy[i+1:]...)
}
}
}
if len(bCopy) == 0 {
return true
}
return false
}
func matchMapIntegerUuidIfNonZero(a, b map[int64]string) bool {
if b == nil {
return true
}
return matchMapIntegerUuid(a, b)
}
func matchMapIntegerUuid(a, b map[int64]string) bool {
if len(a) != len(b) {
return false
}
bCopy := map[int64]string{}
for k, v := range b {
bCopy[k] = v
}
for aK, aV := range a {
if bV, ok := bCopy[aK]; !ok || aV != bV {
return false
} else {
delete(bCopy, aK)
}
}
if len(bCopy) == 0 {
return true
}
return false
}
func matchMapIntegerStringIfNonZero(a, b map[int64]string) bool {
if b == nil {
return true
}
return matchMapIntegerString(a, b)
}
func matchMapIntegerString(a, b map[int64]string) bool {
if len(a) != len(b) {
return false
}
bCopy := map[int64]string{}
for k, v := range b {
bCopy[k] = v
}
for aK, aV := range a {
if bV, ok := bCopy[aK]; !ok || aV != bV {
return false
} else {
delete(bCopy, aK)
}
}
if len(bCopy) == 0 {
return true
}
return false
}
func matchMapIntegerIntegerIfNonZero(a, b map[int64]int64) bool {
if b == nil {
return true
}
return matchMapIntegerInteger(a, b)
}
func matchMapIntegerInteger(a, b map[int64]int64) bool {
if len(a) != len(b) {
return false
}
bCopy := map[int64]int64{}
for k, v := range b {
bCopy[k] = v
}
for aK, aV := range a {
if bV, ok := bCopy[aK]; !ok || aV != bV {
return false
} else {
delete(bCopy, aK)
}
}
if len(bCopy) == 0 {
return true
}
return false
}
func matchMapIntegerBooleanIfNonZero(a, b map[int64]bool) bool {
if b == nil {
return true
}
return matchMapIntegerBoolean(a, b)
}
func matchMapIntegerBoolean(a, b map[int64]bool) bool {
if len(a) != len(b) {
return false
}
bCopy := map[int64]bool{}
for k, v := range b {
bCopy[k] = v
}
for aK, aV := range a {
if bV, ok := bCopy[aK]; !ok || aV != bV {
return false
} else {
delete(bCopy, aK)
}
}
if len(bCopy) == 0 {
return true
}
return false
}
func matchMapIntegerRealIfNonZero(a, b map[int64]float64) bool {
if b == nil {
return true
}
return matchMapIntegerReal(a, b)
}
func matchMapIntegerReal(a, b map[int64]float64) bool {
if len(a) != len(b) {
return false
}
bCopy := map[int64]float64{}
for k, v := range b {
bCopy[k] = v
}
for aK, aV := range a {
if bV, ok := bCopy[aK]; !ok || aV != bV {
return false
} else {
delete(bCopy, aK)
}
}
if len(bCopy) == 0 {
return true
}
return false
}
func matchBooleanIfNonZero(a, b bool) bool {
var z bool
if b == z {
return true
}
return matchBoolean(a, b)
}
func matchBoolean(a, b bool) bool {
return a == b
}
func matchBooleanOptionalIfNonZero(a, b *bool) bool {
if b == nil {
return true
}
return matchBooleanOptional(a, b)
}
func matchBooleanOptional(a, b *bool) bool {
if a == nil && b == nil {
return true
} else if a != nil && b != nil {
return *a == *b
}
return false
}
func matchBooleanMultiplesIfNonZero(a, b []bool) bool {
if b == nil {
return true
}
return matchBooleanMultiples(a, b)
}
func matchBooleanMultiples(a, b []bool) bool {
if len(a) != len(b) {
return false
}
bCopy := make([]bool, len(b))
copy(bCopy, b)
for _, elA := range a {
for i := len(bCopy) - 1; i >= 0; i-- {
elB := bCopy[i]
if elA == elB {
bCopy = append(bCopy[:i], bCopy[i+1:]...)
}
}
}
if len(bCopy) == 0 {
return true
}
return false
}
func matchMapBooleanBooleanIfNonZero(a, b map[bool]bool) bool {
if b == nil {
return true
}
return matchMapBooleanBoolean(a, b)
}
func matchMapBooleanBoolean(a, b map[bool]bool) bool {
if len(a) != len(b) {
return false
}
bCopy := map[bool]bool{}
for k, v := range b {
bCopy[k] = v
}
for aK, aV := range a {
if bV, ok := bCopy[aK]; !ok || aV != bV {
return false
} else {
delete(bCopy, aK)
}
}
if len(bCopy) == 0 {
return true
}
return false
}
func matchMapBooleanRealIfNonZero(a, b map[bool]float64) bool {
if b == nil {
return true
}
return matchMapBooleanReal(a, b)
}
func matchMapBooleanReal(a, b map[bool]float64) bool {
if len(a) != len(b) {
return false
}
bCopy := map[bool]float64{}
for k, v := range b {
bCopy[k] = v
}
for aK, aV := range a {
if bV, ok := bCopy[aK]; !ok || aV != bV {
return false
} else {
delete(bCopy, aK)
}
}
if len(bCopy) == 0 {
return true
}
return false
}
func matchMapBooleanUuidIfNonZero(a, b map[bool]string) bool {
if b == nil {
return true
}
return matchMapBooleanUuid(a, b)
}
func matchMapBooleanUuid(a, b map[bool]string) bool {
if len(a) != len(b) {
return false
}
bCopy := map[bool]string{}
for k, v := range b {
bCopy[k] = v
}
for aK, aV := range a {
if bV, ok := bCopy[aK]; !ok || aV != bV {
return false
} else {
delete(bCopy, aK)
}
}
if len(bCopy) == 0 {
return true
}
return false
}
func matchMapBooleanStringIfNonZero(a, b map[bool]string) bool {
if b == nil {
return true
}
return matchMapBooleanString(a, b)
}
func matchMapBooleanString(a, b map[bool]string) bool {
if len(a) != len(b) {
return false
}
bCopy := map[bool]string{}
for k, v := range b {
bCopy[k] = v
}
for aK, aV := range a {
if bV, ok := bCopy[aK]; !ok || aV != bV {
return false
} else {
delete(bCopy, aK)
}
}
if len(bCopy) == 0 {
return true
}
return false
}
func matchMapBooleanIntegerIfNonZero(a, b map[bool]int64) bool {
if b == nil {
return true
}
return matchMapBooleanInteger(a, b)
}
func matchMapBooleanInteger(a, b map[bool]int64) bool {
if len(a) != len(b) {
return false
}
bCopy := map[bool]int64{}
for k, v := range b {
bCopy[k] = v
}
for aK, aV := range a {
if bV, ok := bCopy[aK]; !ok || aV != bV {
return false
} else {
delete(bCopy, aK)
}
}
if len(bCopy) == 0 {
return true
}
return false
}
func matchRealIfNonZero(a, b float64) bool {
var z float64
if b == z {
return true
}
return matchReal(a, b)
}
func matchReal(a, b float64) bool {
return a == b
}
func matchRealOptionalIfNonZero(a, b *float64) bool {
if b == nil {
return true
}
return matchRealOptional(a, b)
}
func matchRealOptional(a, b *float64) bool {
if a == nil && b == nil {
return true
} else if a != nil && b != nil {
return *a == *b
}
return false
}
func matchRealMultiplesIfNonZero(a, b []float64) bool {
if b == nil {
return true
}
return matchRealMultiples(a, b)
}
func matchRealMultiples(a, b []float64) bool {
if len(a) != len(b) {
return false
}
bCopy := make([]float64, len(b))
copy(bCopy, b)
for _, elA := range a {
for i := len(bCopy) - 1; i >= 0; i-- {
elB := bCopy[i]
if elA == elB {
bCopy = append(bCopy[:i], bCopy[i+1:]...)
}
}
}
if len(bCopy) == 0 {
return true
}
return false
}
func matchMapRealUuidIfNonZero(a, b map[float64]string) bool {
if b == nil {
return true
}
return matchMapRealUuid(a, b)
}
func matchMapRealUuid(a, b map[float64]string) bool {
if len(a) != len(b) {
return false
}
bCopy := map[float64]string{}
for k, v := range b {
bCopy[k] = v
}
for aK, aV := range a {
if bV, ok := bCopy[aK]; !ok || aV != bV {
return false
} else {
delete(bCopy, aK)
}
}
if len(bCopy) == 0 {
return true
}
return false
}
func matchMapRealStringIfNonZero(a, b map[float64]string) bool {
if b == nil {
return true
}
return matchMapRealString(a, b)
}
func matchMapRealString(a, b map[float64]string) bool {
if len(a) != len(b) {
return false
}
bCopy := map[float64]string{}
for k, v := range b {
bCopy[k] = v
}
for aK, aV := range a {
if bV, ok := bCopy[aK]; !ok || aV != bV {
return false
} else {
delete(bCopy, aK)
}
}
if len(bCopy) == 0 {
return true
}
return false
}
func matchMapRealIntegerIfNonZero(a, b map[float64]int64) bool {
if b == nil {
return true
}
return matchMapRealInteger(a, b)
}
func matchMapRealInteger(a, b map[float64]int64) bool {
if len(a) != len(b) {
return false
}
bCopy := map[float64]int64{}
for k, v := range b {
bCopy[k] = v
}
for aK, aV := range a {
if bV, ok := bCopy[aK]; !ok || aV != bV {
return false
} else {
delete(bCopy, aK)
}
}
if len(bCopy) == 0 {
return true
}
return false
}
func matchMapRealBooleanIfNonZero(a, b map[float64]bool) bool {
if b == nil {
return true
}
return matchMapRealBoolean(a, b)
}
func matchMapRealBoolean(a, b map[float64]bool) bool {
if len(a) != len(b) {
return false
}
bCopy := map[float64]bool{}
for k, v := range b {
bCopy[k] = v
}
for aK, aV := range a {
if bV, ok := bCopy[aK]; !ok || aV != bV {
return false
} else {
delete(bCopy, aK)
}
}
if len(bCopy) == 0 {
return true
}
return false
}
func matchMapRealRealIfNonZero(a, b map[float64]float64) bool {
if b == nil {
return true
}
return matchMapRealReal(a, b)
}
func matchMapRealReal(a, b map[float64]float64) bool {
if len(a) != len(b) {
return false
}
bCopy := map[float64]float64{}
for k, v := range b {
bCopy[k] = v
}
for aK, aV := range a {
if bV, ok := bCopy[aK]; !ok || aV != bV {
return false
} else {
delete(bCopy, aK)
}
}
if len(bCopy) == 0 {
return true
}
return false
}
func matchUuidIfNonZero(a, b string) bool {
var z string
if b == z {
return true
}
return matchUuid(a, b)
}
func matchUuid(a, b string) bool {
return a == b
}
func matchUuidOptionalIfNonZero(a, b *string) bool {
if b == nil {
return true
}
return matchUuidOptional(a, b)
}
func matchUuidOptional(a, b *string) bool {
if a == nil && b == nil {
return true
} else if a != nil && b != nil {
return *a == *b
}
return false
}
func matchUuidMultiplesIfNonZero(a, b []string) bool {
if b == nil {
return true
}
return matchUuidMultiples(a, b)
}
func matchUuidMultiples(a, b []string) bool {
if len(a) != len(b) {
return false
}
bCopy := make([]string, len(b))
copy(bCopy, b)
for _, elA := range a {
for i := len(bCopy) - 1; i >= 0; i-- {
elB := bCopy[i]
if elA == elB {
bCopy = append(bCopy[:i], bCopy[i+1:]...)
}
}
}
if len(bCopy) == 0 {
return true
}
return false
}
func matchMapUuidBooleanIfNonZero(a, b map[string]bool) bool {
if b == nil {
return true
}
return matchMapUuidBoolean(a, b)
}
func matchMapUuidBoolean(a, b map[string]bool) bool {
if len(a) != len(b) {
return false
}
bCopy := map[string]bool{}
for k, v := range b {
bCopy[k] = v
}
for aK, aV := range a {
if bV, ok := bCopy[aK]; !ok || aV != bV {
return false
} else {
delete(bCopy, aK)
}
}
if len(bCopy) == 0 {
return true
}
return false
}
func matchMapUuidRealIfNonZero(a, b map[string]float64) bool {
if b == nil {
return true
}
return matchMapUuidReal(a, b)
}
func matchMapUuidReal(a, b map[string]float64) bool {
if len(a) != len(b) {
return false
}
bCopy := map[string]float64{}
for k, v := range b {
bCopy[k] = v
}
for aK, aV := range a {
if bV, ok := bCopy[aK]; !ok || aV != bV {
return false
} else {
delete(bCopy, aK)
}
}
if len(bCopy) == 0 {
return true
}
return false
}
func matchMapUuidUuidIfNonZero(a, b map[string]string) bool {
if b == nil {
return true
}
return matchMapUuidUuid(a, b)
}
func matchMapUuidUuid(a, b map[string]string) bool {
if len(a) != len(b) {
return false
}
bCopy := map[string]string{}
for k, v := range b {
bCopy[k] = v
}
for aK, aV := range a {
if bV, ok := bCopy[aK]; !ok || aV != bV {
return false
} else {
delete(bCopy, aK)
}
}
if len(bCopy) == 0 {
return true
}
return false
}
func matchMapUuidStringIfNonZero(a, b map[string]string) bool {
if b == nil {
return true
}
return matchMapUuidString(a, b)
}
func matchMapUuidString(a, b map[string]string) bool {
if len(a) != len(b) {
return false
}
bCopy := map[string]string{}
for k, v := range b {
bCopy[k] = v
}
for aK, aV := range a {
if bV, ok := bCopy[aK]; !ok || aV != bV {
return false
} else {
delete(bCopy, aK)
}
}
if len(bCopy) == 0 {
return true
}
return false
}
func matchMapUuidIntegerIfNonZero(a, b map[string]int64) bool {
if b == nil {
return true
}
return matchMapUuidInteger(a, b)
}
func matchMapUuidInteger(a, b map[string]int64) bool {
if len(a) != len(b) {
return false
}
bCopy := map[string]int64{}
for k, v := range b {
bCopy[k] = v
}
for aK, aV := range a {
if bV, ok := bCopy[aK]; !ok || aV != bV {
return false
} else {
delete(bCopy, aK)
}
}
if len(bCopy) == 0 {
return true
}
return false
}
func matchStringIfNonZero(a, b string) bool {
var z string
if b == z {
return true
}
return matchString(a, b)
}
func matchString(a, b string) bool {
return a == b
}
func matchStringOptionalIfNonZero(a, b *string) bool {
if b == nil {
return true
}
return matchStringOptional(a, b)
}
func matchStringOptional(a, b *string) bool {
if a == nil && b == nil {
return true
} else if a != nil && b != nil {
return *a == *b
}
return false
}
func matchStringMultiplesIfNonZero(a, b []string) bool {
if b == nil {
return true
}
return matchStringMultiples(a, b)
}
func matchStringMultiples(a, b []string) bool {
if len(a) != len(b) {
return false
}
bCopy := make([]string, len(b))
copy(bCopy, b)
for _, elA := range a {
for i := len(bCopy) - 1; i >= 0; i-- {
elB := bCopy[i]
if elA == elB {
bCopy = append(bCopy[:i], bCopy[i+1:]...)
}
}
}
if len(bCopy) == 0 {
return true
}
return false
}
func matchMapStringStringIfNonZero(a, b map[string]string) bool {
if b == nil {
return true
}
return matchMapStringString(a, b)
}
func matchMapStringString(a, b map[string]string) bool {
if len(a) != len(b) {
return false
}
bCopy := map[string]string{}
for k, v := range b {
bCopy[k] = v
}
for aK, aV := range a {
if bV, ok := bCopy[aK]; !ok || aV != bV {
return false
} else {
delete(bCopy, aK)
}
}
if len(bCopy) == 0 {
return true
}
return false
}
func matchMapStringIntegerIfNonZero(a, b map[string]int64) bool {
if b == nil {
return true
}
return matchMapStringInteger(a, b)
}
func matchMapStringInteger(a, b map[string]int64) bool {
if len(a) != len(b) {
return false
}
bCopy := map[string]int64{}
for k, v := range b {
bCopy[k] = v
}
for aK, aV := range a {
if bV, ok := bCopy[aK]; !ok || aV != bV {
return false
} else {
delete(bCopy, aK)
}
}
if len(bCopy) == 0 {
return true
}
return false
}
func matchMapStringBooleanIfNonZero(a, b map[string]bool) bool {
if b == nil {
return true
}
return matchMapStringBoolean(a, b)
}
func matchMapStringBoolean(a, b map[string]bool) bool {
if len(a) != len(b) {
return false
}
bCopy := map[string]bool{}
for k, v := range b {
bCopy[k] = v
}
for aK, aV := range a {
if bV, ok := bCopy[aK]; !ok || aV != bV {
return false
} else {
delete(bCopy, aK)
}
}
if len(bCopy) == 0 {
return true
}
return false
}
func matchMapStringRealIfNonZero(a, b map[string]float64) bool {
if b == nil {
return true
}
return matchMapStringReal(a, b)
}
func matchMapStringReal(a, b map[string]float64) bool {
if len(a) != len(b) {
return false
}
bCopy := map[string]float64{}
for k, v := range b {
bCopy[k] = v
}
for aK, aV := range a {
if bV, ok := bCopy[aK]; !ok || aV != bV {
return false
} else {
delete(bCopy, aK)
}
}
if len(bCopy) == 0 {
return true
}
return false
}
func matchMapStringUuidIfNonZero(a, b map[string]string) bool {
if b == nil {
return true
}
return matchMapStringUuid(a, b)
}
func matchMapStringUuid(a, b map[string]string) bool {
if len(a) != len(b) {
return false
}
bCopy := map[string]string{}
for k, v := range b {
bCopy[k] = v
}
for aK, aV := range a {
if bV, ok := bCopy[aK]; !ok || aV != bV {
return false
} else {
delete(bCopy, aK)
}
}
if len(bCopy) == 0 {
return true
}
return false
}
+425
View File
@@ -0,0 +1,425 @@
// 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 ovnutil
import (
"fmt"
"strings"
)
func OvnArgUuid(a string) string {
return fmt.Sprintf("%s", a)
}
func OvnArgsUuid(field string, a string) []string {
return []string{fmt.Sprintf("%s=%s", field, OvnArgUuid(a))}
}
func OvnArgsUuidOptional(field string, a *string) []string {
if a == nil {
return nil
}
return OvnArgsUuid(field, *a)
}
func OvnArgsUuidMultiples(field string, a []string) []string {
if len(a) == 0 {
return nil
}
elArgs := make([]string, len(a))
for i, el := range a {
elArgs[i] = OvnArgUuid(el)
}
arg := fmt.Sprintf("%s=[%s]", field, strings.Join(elArgs, ","))
return []string{arg}
}
func OvnArgsMapUuidReal(field string, a map[string]float64) []string {
if len(a) == 0 {
return nil
}
r := make([]string, 0, len(a))
for aK, aV := range a {
r = append(r, fmt.Sprintf("%s:%s=%s", field, OvnArgUuid(aK), OvnArgReal(aV)))
}
return r
}
func OvnArgsMapUuidUuid(field string, a map[string]string) []string {
if len(a) == 0 {
return nil
}
r := make([]string, 0, len(a))
for aK, aV := range a {
r = append(r, fmt.Sprintf("%s:%s=%s", field, OvnArgUuid(aK), OvnArgUuid(aV)))
}
return r
}
func OvnArgsMapUuidString(field string, a map[string]string) []string {
if len(a) == 0 {
return nil
}
r := make([]string, 0, len(a))
for aK, aV := range a {
r = append(r, fmt.Sprintf("%s:%s=%s", field, OvnArgUuid(aK), OvnArgString(aV)))
}
return r
}
func OvnArgsMapUuidInteger(field string, a map[string]int64) []string {
if len(a) == 0 {
return nil
}
r := make([]string, 0, len(a))
for aK, aV := range a {
r = append(r, fmt.Sprintf("%s:%s=%s", field, OvnArgUuid(aK), OvnArgInteger(aV)))
}
return r
}
func OvnArgsMapUuidBoolean(field string, a map[string]bool) []string {
if len(a) == 0 {
return nil
}
r := make([]string, 0, len(a))
for aK, aV := range a {
r = append(r, fmt.Sprintf("%s:%s=%s", field, OvnArgUuid(aK), OvnArgBoolean(aV)))
}
return r
}
func OvnArgString(a string) string {
return fmt.Sprintf("%q", a)
}
func OvnArgsString(field string, a string) []string {
return []string{fmt.Sprintf("%s=%s", field, OvnArgString(a))}
}
func OvnArgsStringOptional(field string, a *string) []string {
if a == nil {
return nil
}
return OvnArgsString(field, *a)
}
func OvnArgsStringMultiples(field string, a []string) []string {
if len(a) == 0 {
return nil
}
elArgs := make([]string, len(a))
for i, el := range a {
elArgs[i] = OvnArgString(el)
}
arg := fmt.Sprintf("%s=[%s]", field, strings.Join(elArgs, ","))
return []string{arg}
}
func OvnArgsMapStringUuid(field string, a map[string]string) []string {
if len(a) == 0 {
return nil
}
r := make([]string, 0, len(a))
for aK, aV := range a {
r = append(r, fmt.Sprintf("%s:%s=%s", field, OvnArgString(aK), OvnArgUuid(aV)))
}
return r
}
func OvnArgsMapStringString(field string, a map[string]string) []string {
if len(a) == 0 {
return nil
}
r := make([]string, 0, len(a))
for aK, aV := range a {
r = append(r, fmt.Sprintf("%s:%s=%s", field, OvnArgString(aK), OvnArgString(aV)))
}
return r
}
func OvnArgsMapStringInteger(field string, a map[string]int64) []string {
if len(a) == 0 {
return nil
}
r := make([]string, 0, len(a))
for aK, aV := range a {
r = append(r, fmt.Sprintf("%s:%s=%s", field, OvnArgString(aK), OvnArgInteger(aV)))
}
return r
}
func OvnArgsMapStringBoolean(field string, a map[string]bool) []string {
if len(a) == 0 {
return nil
}
r := make([]string, 0, len(a))
for aK, aV := range a {
r = append(r, fmt.Sprintf("%s:%s=%s", field, OvnArgString(aK), OvnArgBoolean(aV)))
}
return r
}
func OvnArgsMapStringReal(field string, a map[string]float64) []string {
if len(a) == 0 {
return nil
}
r := make([]string, 0, len(a))
for aK, aV := range a {
r = append(r, fmt.Sprintf("%s:%s=%s", field, OvnArgString(aK), OvnArgReal(aV)))
}
return r
}
func OvnArgInteger(a int64) string {
return fmt.Sprintf("%d", a)
}
func OvnArgsInteger(field string, a int64) []string {
return []string{fmt.Sprintf("%s=%s", field, OvnArgInteger(a))}
}
func OvnArgsIntegerOptional(field string, a *int64) []string {
if a == nil {
return nil
}
return OvnArgsInteger(field, *a)
}
func OvnArgsIntegerMultiples(field string, a []int64) []string {
if len(a) == 0 {
return nil
}
elArgs := make([]string, len(a))
for i, el := range a {
elArgs[i] = OvnArgInteger(el)
}
arg := fmt.Sprintf("%s=[%s]", field, strings.Join(elArgs, ","))
return []string{arg}
}
func OvnArgsMapIntegerUuid(field string, a map[int64]string) []string {
if len(a) == 0 {
return nil
}
r := make([]string, 0, len(a))
for aK, aV := range a {
r = append(r, fmt.Sprintf("%s:%s=%s", field, OvnArgInteger(aK), OvnArgUuid(aV)))
}
return r
}
func OvnArgsMapIntegerString(field string, a map[int64]string) []string {
if len(a) == 0 {
return nil
}
r := make([]string, 0, len(a))
for aK, aV := range a {
r = append(r, fmt.Sprintf("%s:%s=%s", field, OvnArgInteger(aK), OvnArgString(aV)))
}
return r
}
func OvnArgsMapIntegerInteger(field string, a map[int64]int64) []string {
if len(a) == 0 {
return nil
}
r := make([]string, 0, len(a))
for aK, aV := range a {
r = append(r, fmt.Sprintf("%s:%s=%s", field, OvnArgInteger(aK), OvnArgInteger(aV)))
}
return r
}
func OvnArgsMapIntegerBoolean(field string, a map[int64]bool) []string {
if len(a) == 0 {
return nil
}
r := make([]string, 0, len(a))
for aK, aV := range a {
r = append(r, fmt.Sprintf("%s:%s=%s", field, OvnArgInteger(aK), OvnArgBoolean(aV)))
}
return r
}
func OvnArgsMapIntegerReal(field string, a map[int64]float64) []string {
if len(a) == 0 {
return nil
}
r := make([]string, 0, len(a))
for aK, aV := range a {
r = append(r, fmt.Sprintf("%s:%s=%s", field, OvnArgInteger(aK), OvnArgReal(aV)))
}
return r
}
func OvnArgBoolean(a bool) string {
return fmt.Sprintf("%v", a)
}
func OvnArgsBoolean(field string, a bool) []string {
return []string{fmt.Sprintf("%s=%s", field, OvnArgBoolean(a))}
}
func OvnArgsBooleanOptional(field string, a *bool) []string {
if a == nil {
return nil
}
return OvnArgsBoolean(field, *a)
}
func OvnArgsBooleanMultiples(field string, a []bool) []string {
if len(a) == 0 {
return nil
}
elArgs := make([]string, len(a))
for i, el := range a {
elArgs[i] = OvnArgBoolean(el)
}
arg := fmt.Sprintf("%s=[%s]", field, strings.Join(elArgs, ","))
return []string{arg}
}
func OvnArgsMapBooleanUuid(field string, a map[bool]string) []string {
if len(a) == 0 {
return nil
}
r := make([]string, 0, len(a))
for aK, aV := range a {
r = append(r, fmt.Sprintf("%s:%s=%s", field, OvnArgBoolean(aK), OvnArgUuid(aV)))
}
return r
}
func OvnArgsMapBooleanString(field string, a map[bool]string) []string {
if len(a) == 0 {
return nil
}
r := make([]string, 0, len(a))
for aK, aV := range a {
r = append(r, fmt.Sprintf("%s:%s=%s", field, OvnArgBoolean(aK), OvnArgString(aV)))
}
return r
}
func OvnArgsMapBooleanInteger(field string, a map[bool]int64) []string {
if len(a) == 0 {
return nil
}
r := make([]string, 0, len(a))
for aK, aV := range a {
r = append(r, fmt.Sprintf("%s:%s=%s", field, OvnArgBoolean(aK), OvnArgInteger(aV)))
}
return r
}
func OvnArgsMapBooleanBoolean(field string, a map[bool]bool) []string {
if len(a) == 0 {
return nil
}
r := make([]string, 0, len(a))
for aK, aV := range a {
r = append(r, fmt.Sprintf("%s:%s=%s", field, OvnArgBoolean(aK), OvnArgBoolean(aV)))
}
return r
}
func OvnArgsMapBooleanReal(field string, a map[bool]float64) []string {
if len(a) == 0 {
return nil
}
r := make([]string, 0, len(a))
for aK, aV := range a {
r = append(r, fmt.Sprintf("%s:%s=%s", field, OvnArgBoolean(aK), OvnArgReal(aV)))
}
return r
}
func OvnArgReal(a float64) string {
return fmt.Sprintf("%f", a)
}
func OvnArgsReal(field string, a float64) []string {
return []string{fmt.Sprintf("%s=%s", field, OvnArgReal(a))}
}
func OvnArgsRealOptional(field string, a *float64) []string {
if a == nil {
return nil
}
return OvnArgsReal(field, *a)
}
func OvnArgsRealMultiples(field string, a []float64) []string {
if len(a) == 0 {
return nil
}
elArgs := make([]string, len(a))
for i, el := range a {
elArgs[i] = OvnArgReal(el)
}
arg := fmt.Sprintf("%s=[%s]", field, strings.Join(elArgs, ","))
return []string{arg}
}
func OvnArgsMapRealReal(field string, a map[float64]float64) []string {
if len(a) == 0 {
return nil
}
r := make([]string, 0, len(a))
for aK, aV := range a {
r = append(r, fmt.Sprintf("%s:%s=%s", field, OvnArgReal(aK), OvnArgReal(aV)))
}
return r
}
func OvnArgsMapRealUuid(field string, a map[float64]string) []string {
if len(a) == 0 {
return nil
}
r := make([]string, 0, len(a))
for aK, aV := range a {
r = append(r, fmt.Sprintf("%s:%s=%s", field, OvnArgReal(aK), OvnArgUuid(aV)))
}
return r
}
func OvnArgsMapRealString(field string, a map[float64]string) []string {
if len(a) == 0 {
return nil
}
r := make([]string, 0, len(a))
for aK, aV := range a {
r = append(r, fmt.Sprintf("%s:%s=%s", field, OvnArgReal(aK), OvnArgString(aV)))
}
return r
}
func OvnArgsMapRealInteger(field string, a map[float64]int64) []string {
if len(a) == 0 {
return nil
}
r := make([]string, 0, len(a))
for aK, aV := range a {
r = append(r, fmt.Sprintf("%s:%s=%s", field, OvnArgReal(aK), OvnArgInteger(aV)))
}
return r
}
func OvnArgsMapRealBoolean(field string, a map[float64]bool) []string {
if len(a) == 0 {
return nil
}
r := make([]string, 0, len(a))
for aK, aV := range a {
r = append(r, fmt.Sprintf("%s:%s=%s", field, OvnArgReal(aK), OvnArgBoolean(aV)))
}
return r
}
+15
View File
@@ -0,0 +1,15 @@
// 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 ovnutil // import "yunion.io/x/onecloud/pkg/vpcagent/ovnutil"
+167
View File
@@ -0,0 +1,167 @@
// 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 ovnutil
import (
"context"
"fmt"
"os/exec"
"sort"
"strings"
"time"
"yunion.io/x/log"
"yunion.io/x/pkg/errors"
)
const ovnNbCtlTimeout = 8 * time.Second
type CmdResult struct {
Output string
Err error
}
func (res *CmdResult) Error() string {
return fmt.Sprintf("err: %v, output: %s", res.Err, res.Output)
}
type OvnNbCtl struct {
db string
}
func NewOvnNbCtl(db string) *OvnNbCtl {
cli := &OvnNbCtl{
db: db,
}
return cli
}
func (cli *OvnNbCtl) prepArgs(args []string) []string {
var r []string
if cli.db != "" {
r = make([]string, len(args)+1)
r[0] = "--db=" + cli.db
copy(r[1:], args)
} else {
r = args
}
return r
}
func (cli *OvnNbCtl) run(ctx context.Context, args []string) *CmdResult {
ctx, cancel := context.WithTimeout(ctx, ovnNbCtlTimeout)
defer cancel()
args = cli.prepArgs(args)
cmd := exec.CommandContext(ctx, "ovn-nbctl", args...)
combined, err := cmd.CombinedOutput()
res := &CmdResult{
Output: string(combined),
Err: err,
}
return res
}
func (cli *OvnNbCtl) Must(ctx context.Context, msg string, args []string) *CmdResult {
res := cli.run(ctx, args)
if res.Err != nil {
panic(cli.errWrap(res, msg, args))
}
if cli.argsHasWrite(args) {
log.Infof("%s:\n%s", msg, ovnNbctlArgsString(args))
}
return res
}
func (cli *OvnNbCtl) errWrap(err error, msg string, args []string) error {
s := cli.argsString(args)
return errors.Wrapf(err, "%s:\n%s\n", msg, s)
}
func (cli *OvnNbCtl) argsString(args []string) string {
args = cli.prepArgs(args)
s := ovnNbctlArgsString(args)
return s
}
func (cli *OvnNbCtl) argsHasWrite(args []string) bool {
for _, arg := range args {
switch arg {
case "create", "set", "add", "remove", "destroy", "clear":
return true
case "list", "find", "get":
case "lsp-del", "lrp-del":
return true
default:
}
}
return false
}
func ovnNbctlArgsString(args []string) string {
var (
s = ""
indent = ""
indent1 = "\t"
indent2 = "\t\t"
)
s += "ovn-nbctl"
for _, arg := range args {
if arg == "--" {
indent = indent1
s += ` \` + "\n"
s += indent
s += arg
} else if !strings.HasPrefix(arg, "--") && strings.ContainsRune(arg, '=') {
if indent == indent1 {
indent = indent2
}
s += ` \` + "\n"
s += indent
s += fmt.Sprintf("%q", arg)
} else {
s += fmt.Sprintf(" %q", arg)
}
}
return s
}
func OvnNbctlArgsDestroy(irows []IRow) []string {
sort.Slice(irows, func(i, j int) bool {
ri := irows[i]
rj := irows[j]
iri := ri.OvnIsRoot()
irj := rj.OvnIsRoot()
if !iri && irj {
return true
}
return false
})
var args []string
for _, irow := range irows {
switch irow.(type) {
case *LogicalSwitchPort:
args = append(args, "--", "--if-exists", "lsp-del", irow.OvnUuid())
case *LogicalRouterPort:
args = append(args, "--", "--if-exists", "lrp-del", irow.OvnUuid())
default:
if !irow.OvnIsRoot() {
panic(irow.OvnTableName())
}
args = append(args, "--", "--if-exists", "destroy", irow.OvnTableName(), irow.OvnUuid())
}
}
return args
}
File diff suppressed because it is too large Load Diff
+47
View File
@@ -0,0 +1,47 @@
// 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 ovnutil
import (
"reflect"
"testing"
)
func TestUnmarshal(t *testing.T) {
d := `{"data":[[["uuid","281f3163-0430-4626-8b94-eb4e79e6d85a"],["set",[]],["set",[]],["map",[["oc-vpc-id","uuididid"]]],["set",[]],"ls0",["map",[["subnet","192.168.2.0/24"]]],["set",[]],["set",[]]]],"headings":["_uuid","acls","dns_records","external_ids","load_balancer","name","other_config","ports","qos_rules"]}`
got := &LogicalSwitchTable{}
if err := UnmarshalJSON([]byte(d), got); err != nil {
t.Fatalf("unmarshal: %v", err)
}
want := &LogicalSwitchTable{
LogicalSwitch{
Uuid: "281f3163-0430-4626-8b94-eb4e79e6d85a",
Name: "ls0",
ExternalIds: map[string]string{"oc-vpc-id": "uuididid"},
OtherConfig: map[string]string{"subnet": "192.168.2.0/24"},
},
}
if !reflect.DeepEqual(got, want) {
t.Errorf("\n got %#v\nwant %#v", got, want)
}
t.Run("logical switch", func(t *testing.T) {
d := `{"data":[[["uuid","d9dbe3cc-7efe-4cf2-91f0-f8f2143037f1"],"32:38:30:34:61:64 169.254.169.3",["set",[]],["set",[]],["set",[]],["set",[]],["map",[]],"subnet-lsmp-subnet3",["map",[]],["set",[]],["set",[]],["set",[]],["set",[]],"localport",false],[["uuid","90fd42c1-ca9c-4fb5-a37a-a950faef4d19"],"router",["set",[]],["set",[]],["set",[]],["set",[]],["map",[]],"subnet-lsp-subnet3",["map",[["router-port","subnet-lrp-subnet3"]]],["set",[]],["set",[]],["set",[]],["set",[]],"router",true],[["uuid","f8fefe76-083b-405c-ae1e-1e70eab9b903"],"00:33:00:00:00:09 192.168.3.9",["uuid","34466b0c-038d-44df-b444-f127d19ef188"],["set",[]],["set",[]],["set",[]],["map",[]],"iface-p39",["map",[]],["set",[]],"00:33:00:00:00:09 192.168.3.9/24",["set",[]],["set",[]],"",true],[["uuid","e514e6da-080b-46b8-9822-d62596c28918"],"router",["set",[]],["set",[]],["set",[]],["set",[]],["map",[]],"subnet-lsp-subnet2",["map",[["router-port","subnet-lrp-subnet2"]]],["set",[]],["set",[]],["set",[]],["set",[]],"router",true],[["uuid","804d671e-20d4-4ad3-82e8-735463dfed09"],"00:22:00:00:00:04 192.168.2.4",["uuid","2cd3dfeb-c521-4e88-9ffb-c89006f8f92b"],["set",[]],["set",[]],["set",[]],["map",[]],"iface-p24",["map",[]],["set",[]],"00:22:00:00:00:04 192.168.2.4/24",["set",[]],["set",[]],"",true],[["uuid","91742362-93b9-4d06-92df-d5e0e2c28dc3"],"00:22:00:00:00:03 192.168.2.3",["uuid","2cd3dfeb-c521-4e88-9ffb-c89006f8f92b"],["set",[]],["set",[]],["set",[]],["map",[]],"iface-p23",["map",[]],["set",[]],"00:22:00:00:00:03 192.168.2.3/24",["set",[]],["set",[]],"",true],[["uuid","91d9efcf-fba3-4b5b-adaa-4c64948f8824"],"32:31:36:37:62:38 169.254.169.2",["set",[]],["set",[]],["set",[]],["set",[]],["map",[]],"subnet-lsmp-subnet2",["map",[]],["set",[]],["set",[]],["set",[]],["set",[]],"localport",false]],"headings":["_uuid","addresses","dhcpv4_options","dhcpv6_options","dynamic_addresses","enabled","external_ids","name","options","parent_name","port_security","tag","tag_request","type","up"]}`
got := &LogicalSwitchPortTable{}
if err := UnmarshalJSON([]byte(d), got); err != nil {
t.Fatalf("unmarshal: %v", err)
}
})
}
+61
View File
@@ -0,0 +1,61 @@
// 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 ovnutil
import (
"encoding/json"
"yunion.io/x/pkg/errors"
)
type List struct {
Headings ListHeadings
Data []ListDataRow
}
type ListHeadings []string
type ListDataRow []ListDataColumn
type ListDataColumn = interface{}
const (
ErrColumnIndexOverrun = errors.Error("column index overrun")
)
func (h ListHeadings) GetByIndex(i int) (string, error) {
if i < len(h) {
return h[i], nil
}
return "", ErrColumnIndexOverrun
}
func UnmarshalJSON(data []byte, rows ITable) error {
list := &List{}
if err := json.Unmarshal(data, list); err != nil {
return err
}
for _, row := range list.Data {
r := rows.NewRow()
for ci := range row {
col := row[ci]
colName, err := list.Headings.GetByIndex(ci)
if err != nil {
return err
}
if err := r.SetColumn(colName, col); err != nil {
return err
}
}
}
return nil
}
+15
View File
@@ -0,0 +1,15 @@
// 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 worker // import "yunion.io/x/onecloud/pkg/vpcagent/worker"
+45
View File
@@ -0,0 +1,45 @@
// 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 worker
import (
"context"
"fmt"
"yunion.io/x/onecloud/pkg/vpcagent/options"
)
var workers = map[string]NewWorkerFunc{}
type NewWorkerFunc func(opts *options.Options) IWorker
type IWorker interface {
Start(ctx context.Context)
}
func NewWorker(opts *options.Options) IWorker {
n, ok := workers[opts.VpcProvider]
if !ok {
return nil
}
return n(opts)
}
func RegisterNewWorkerFunc(name string, n NewWorkerFunc) {
if _, ok := workers[name]; ok {
panic(fmt.Sprintf("worker %s already registered", name))
}
workers[name] = n
}