mirror of
https://github.com/yunionio/cloudpods.git
synced 2026-09-01 15:07:17 +08:00
Merge pull request #4256 from yousong/feature/yousong-vpc
Feature/yousong vpc
This commit is contained in:
@@ -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()
|
||||
}
|
||||
}
|
||||
@@ -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")
|
||||
|
||||
@@ -70,3 +70,7 @@ type WireListInput struct {
|
||||
type GlobalVpcListInput struct {
|
||||
apis.EnabledStatusStandaloneResourceListInput
|
||||
}
|
||||
|
||||
const (
|
||||
VPC_PROVIDER_OVN = "ovn"
|
||||
)
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
@@ -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
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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, "", "")
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 ""
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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())
|
||||
}
|
||||
@@ -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(),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@ import (
|
||||
)
|
||||
|
||||
type Host struct {
|
||||
EnabledStatusStandaloneResourceBase
|
||||
EnabledStatusStandaloneResource
|
||||
|
||||
Rack string
|
||||
Slots string
|
||||
|
||||
@@ -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())
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
)
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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{
|
||||
|
||||
@@ -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
@@ -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
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -66,6 +66,7 @@ type CandidatePropertyGetter interface {
|
||||
HostSchedtags() []computemodels.SSchedtag
|
||||
Storages() []*api.CandidateStorage
|
||||
Networks() []*api.CandidateNetwork
|
||||
OvnCapable() bool
|
||||
Status() string
|
||||
HostStatus() string
|
||||
Enabled() bool
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
}
|
||||
@@ -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"
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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"
|
||||
@@ -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,
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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"
|
||||
@@ -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
|
||||
}
|
||||
@@ -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"
|
||||
@@ -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
|
||||
}
|
||||
@@ -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,
|
||||
)
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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"
|
||||
@@ -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
@@ -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)
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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"
|
||||
@@ -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
|
||||
}
|
||||
Reference in New Issue
Block a user