From 5b0f36dbcbf0b87e197f0d8445feccfe3cbff995 Mon Sep 17 00:00:00 2001 From: Yousong Zhou Date: Fri, 6 Dec 2019 08:36:05 +0000 Subject: [PATCH 01/24] mcclient: models: map to db models --- pkg/mcclient/models/base.go | 15 +++++++++------ pkg/mcclient/models/hosts.go | 2 +- 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/pkg/mcclient/models/base.go b/pkg/mcclient/models/base.go index 8af73396f7..0af92b0fc4 100644 --- a/pkg/mcclient/models/base.go +++ b/pkg/mcclient/models/base.go @@ -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 +} diff --git a/pkg/mcclient/models/hosts.go b/pkg/mcclient/models/hosts.go index 44a2beb373..57d7b7001b 100644 --- a/pkg/mcclient/models/hosts.go +++ b/pkg/mcclient/models/hosts.go @@ -21,7 +21,7 @@ import ( ) type Host struct { - EnabledStatusStandaloneResourceBase + EnabledStatusStandaloneResource Rack string Slots string From fb682c8e2bddbc824b51820c416e25ac1f453614 Mon Sep 17 00:00:00 2001 From: Yousong Zhou Date: Fri, 6 Dec 2019 09:36:46 +0000 Subject: [PATCH 02/24] mcclient: rename BaseManagerInterface to IBaseManager --- pkg/apigateway/handler/resource.go | 2 +- pkg/mcclient/modulebase/modules.go | 22 +++++++++++----------- pkg/mcclient/modules/cloudnet/common.go | 2 +- pkg/mcclient/modules/register.go | 12 ++++++------ 4 files changed, 19 insertions(+), 19 deletions(-) diff --git a/pkg/apigateway/handler/resource.go b/pkg/apigateway/handler/resource.go index 80ab51d709..bab13ae7a6 100644 --- a/pkg/apigateway/handler/resource.go +++ b/pkg/apigateway/handler/resource.go @@ -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") diff --git a/pkg/mcclient/modulebase/modules.go b/pkg/mcclient/modulebase/modules.go index 56e3017762..4a2619e29b 100644 --- a/pkg/mcclient/modulebase/modules.go +++ b/pkg/mcclient/modulebase/modules.go @@ -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 / e.g GET /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()) diff --git a/pkg/mcclient/modules/cloudnet/common.go b/pkg/mcclient/modules/cloudnet/common.go index 930c78ee3d..47682d4cb6 100644 --- a/pkg/mcclient/modules/cloudnet/common.go +++ b/pkg/mcclient/modules/cloudnet/common.go @@ -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) } ) diff --git a/pkg/mcclient/modules/register.go b/pkg/mcclient/modules/register.go index 2b93c874a1..11bd133f30 100644 --- a/pkg/mcclient/modules/register.go +++ b/pkg/mcclient/modules/register.go @@ -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) } From e9e8cd1519a841ea69dfc6c3c6ae12382b3acc51 Mon Sep 17 00:00:00 2001 From: Yousong Zhou Date: Tue, 17 Dec 2019 09:19:03 +0000 Subject: [PATCH 03/24] models: guard against nil memory access --- pkg/cloudcommon/db/jointbase.go | 8 ++++++-- pkg/compute/models/cloudproviders.go | 1 - pkg/compute/models/networks.go | 8 +++++--- 3 files changed, 11 insertions(+), 6 deletions(-) diff --git a/pkg/cloudcommon/db/jointbase.go b/pkg/cloudcommon/db/jointbase.go index 73a5017eb6..a4ac7c82c8 100644 --- a/pkg/cloudcommon/db/jointbase.go +++ b/pkg/cloudcommon/db/jointbase.go @@ -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 } diff --git a/pkg/compute/models/cloudproviders.go b/pkg/compute/models/cloudproviders.go index 56eaa45b6c..c933d5abf9 100644 --- a/pkg/compute/models/cloudproviders.go +++ b/pkg/compute/models/cloudproviders.go @@ -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) diff --git a/pkg/compute/models/networks.go b/pkg/compute/models/networks.go index 073f328e2e..f9f937072c 100644 --- a/pkg/compute/models/networks.go +++ b/pkg/compute/models/networks.go @@ -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 From 0a3c8acbd4851dd7dedeac02b3fa7b9b8fe18c2c Mon Sep 17 00:00:00 2001 From: Yousong Zhou Date: Tue, 22 Oct 2019 09:47:05 +0000 Subject: [PATCH 04/24] =?UTF-8?q?vpcs:=20=E5=85=81=E8=AE=B8=E4=B8=BA?= =?UTF-8?q?=E7=A7=81=E6=9C=89=E4=BA=91=E5=88=9B=E5=BB=BAvpc?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pkg/compute/models/vpcs.go | 2 +- pkg/compute/regiondrivers/kvm.go | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/pkg/compute/models/vpcs.go b/pkg/compute/models/vpcs.go index de6e1f3f75..a0d5d98652 100644 --- a/pkg/compute/models/vpcs.go +++ b/pkg/compute/models/vpcs.go @@ -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") diff --git a/pkg/compute/regiondrivers/kvm.go b/pkg/compute/regiondrivers/kvm.go index 4185598b99..0722a21788 100644 --- a/pkg/compute/regiondrivers/kvm.go +++ b/pkg/compute/regiondrivers/kvm.go @@ -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" ) @@ -785,6 +786,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 } From 81dc7fd91e79f4572f0995ece023c19beacfb758 Mon Sep 17 00:00:00 2001 From: Yousong Zhou Date: Fri, 13 Dec 2019 09:35:29 +0000 Subject: [PATCH 05/24] networks: auto-create zone wire --- pkg/compute/models/networks.go | 10 +++++++++- pkg/compute/models/vpcs.go | 17 +++++++++++++++++ 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/pkg/compute/models/networks.go b/pkg/compute/models/networks.go index f9f937072c..de49df4e37 100644 --- a/pkg/compute/models/networks.go +++ b/pkg/compute/models/networks.go @@ -1374,7 +1374,15 @@ func (manager *SNetworkManager) ValidateCreateData(ctx context.Context, userCred 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) + if region.Provider == api.CLOUD_PROVIDER_ONECLOUD { + wire, err := vpc.initWire(ctx, zone) + if err != nil { + return input, httperrors.NewNotFoundError("init wire for zone %s and vpc %s: %v", input.Zone, input.Vpc, err) + } + input.WireId = wire.Id + } else { + 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 { diff --git a/pkg/compute/models/vpcs.go b/pkg/compute/models/vpcs.go index a0d5d98652..f0ad6f4eec 100644 --- a/pkg/compute/models/vpcs.go +++ b/pkg/compute/models/vpcs.go @@ -927,3 +927,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 +} From 93c2192b778f3f17c0cd9242f663f8b1c7cdc90a Mon Sep 17 00:00:00 2001 From: Yousong Zhou Date: Mon, 18 Nov 2019 13:42:56 +0000 Subject: [PATCH 06/24] =?UTF-8?q?vpcs:=20=E8=87=AA=E5=8A=A8=E5=88=A0?= =?UTF-8?q?=E9=99=A4wire?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pkg/compute/models/vpcs.go | 2 +- pkg/compute/models/wires.go | 17 +++++++++++++++++ 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/pkg/compute/models/vpcs.go b/pkg/compute/models/vpcs.go index f0ad6f4eec..65edba3b67 100644 --- a/pkg/compute/models/vpcs.go +++ b/pkg/compute/models/vpcs.go @@ -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) diff --git a/pkg/compute/models/wires.go b/pkg/compute/models/wires.go index bc76c88f4c..df4d7eb41c 100644 --- a/pkg/compute/models/wires.go +++ b/pkg/compute/models/wires.go @@ -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) +} From f194ad718379dc25268581bb2fcf9cc866dfdcba Mon Sep 17 00:00:00 2001 From: Yousong Zhou Date: Fri, 13 Dec 2019 09:36:32 +0000 Subject: [PATCH 07/24] vpcs: add method containsIPV4Range() --- pkg/compute/models/networks.go | 29 +++-------------------------- pkg/compute/models/vpcs.go | 10 ++++++++++ 2 files changed, 13 insertions(+), 26 deletions(-) diff --git a/pkg/compute/models/networks.go b/pkg/compute/models/networks.go index de49df4e37..aef9f7de24 100644 --- a/pkg/compute/models/networks.go +++ b/pkg/compute/models/networks.go @@ -1412,19 +1412,8 @@ func (manager *SNetworkManager) ValidateCreateData(ctx context.Context, userCred return input, httperrors.NewInvalidStatusError("VPC not ready") } - vpcRanges := vpc.getIPRanges() - netRange := netutils.NewIPV4AddrRange(startIp, endIp) - - inRange := false - for _, vpcRange := range vpcRanges { - if vpcRange.ContainsRange(netRange) { - inRange = true - break - } - } - - if !inRange { + if !vpc.containsIPV4Range(netRange) { return input, httperrors.NewInputParameterError("Network not in range of VPC cidrblock %s", vpc.CidrBlock) } @@ -1485,21 +1474,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) } diff --git a/pkg/compute/models/vpcs.go b/pkg/compute/models/vpcs.go index 65edba3b67..b1dec2e8ea 100644 --- a/pkg/compute/models/vpcs.go +++ b/pkg/compute/models/vpcs.go @@ -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") } From d9d343822f193ee4d834cb1eeb11c029914c6107 Mon Sep 17 00:00:00 2001 From: Yousong Zhou Date: Fri, 13 Dec 2019 09:40:42 +0000 Subject: [PATCH 08/24] networks: validate onecloud vpc subnet Also rewrite for more readability, especially the part on finding wire, vpc, region, etc. --- pkg/compute/models/networks.go | 295 ++++++++++++++++++--------------- 1 file changed, 164 insertions(+), 131 deletions(-) diff --git a/pkg/compute/models/networks.go b/pkg/compute/models/networks.go index aef9f7de24..605255b17e 100644 --- a/pkg/compute/models/networks.go +++ b/pkg/compute/models/networks.go @@ -1252,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" { @@ -1313,116 +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 { - if region.Provider == api.CLOUD_PROVIDER_ONECLOUD { - wire, err := vpc.initWire(ctx, zone) - if err != nil { - return input, httperrors.NewNotFoundError("init wire for zone %s and vpc %s: %v", input.Zone, input.Vpc, err) - } - input.WireId = wire.Id - } else { - 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") } - netRange := netutils.NewIPV4AddrRange(startIp, endIp) - if !vpc.containsIPV4Range(netRange) { - return input, httperrors.NewInputParameterError("Network not in range of VPC cidrblock %s", vpc.CidrBlock) + 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() } - 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) + { + 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") + } } + 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 @@ -1465,7 +1498,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") } @@ -1527,9 +1560,9 @@ func (self *SNetwork) ValidateUpdateData(ctx context.Context, userCred mcclient. 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) } From 282da02f9f871928706f996d24345703a6e52887 Mon Sep 17 00:00:00 2001 From: Yousong Zhou Date: Wed, 11 Dec 2019 16:43:25 +0000 Subject: [PATCH 09/24] networks: forbid updating onecloud vpc network --- pkg/compute/models/networks.go | 41 +++++++++++++++++++++++----------- 1 file changed, 28 insertions(+), 13 deletions(-) diff --git a/pkg/compute/models/networks.go b/pkg/compute/models/networks.go index 605255b17e..ab53c4fcb9 100644 --- a/pkg/compute/models/networks.go +++ b/pkg/compute/models/networks.go @@ -1463,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 @@ -1471,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 { @@ -1523,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) @@ -1540,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 { @@ -1556,6 +1544,24 @@ 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) } @@ -1688,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 { From 5f623cb8921a670fd6a27ae6217f0e9f7fa9149b Mon Sep 17 00:00:00 2001 From: Yousong Zhou Date: Thu, 2 Jan 2020 15:18:15 +0800 Subject: [PATCH 10/24] hosts: simplify GetNetworkWithIdAndCredential By removing unused arg --- pkg/compute/guestdrivers/virtualization.go | 2 +- pkg/compute/models/hosts.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pkg/compute/guestdrivers/virtualization.go b/pkg/compute/guestdrivers/virtualization.go index 817d049c82..c5648dce9a 100644 --- a/pkg/compute/guestdrivers/virtualization.go +++ b/pkg/compute/guestdrivers/virtualization.go @@ -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, diff --git a/pkg/compute/models/hosts.go b/pkg/compute/models/hosts.go index a9b5f0fca8..2b2fd50b32 100644 --- a/pkg/compute/models/hosts.go +++ b/pkg/compute/models/hosts.go @@ -1983,7 +1983,7 @@ func (self *SHost) GetNetinterfacesWithIdAndCredential(netId string, userCred mc return nil, nil } -func (self *SHost) GetNetworkWithIdAndCredential(netId string, userCred mcclient.TokenCredential, reserved bool) (*SNetwork, error) { +func (self *SHost) GetNetworkWithId(netId string, reserved bool) (*SNetwork, error) { networks := NetworkManager.Query().SubQuery() hostwires := HostwireManager.Query().SubQuery() hosts := HostManager.Query().SubQuery() From 2fbe4a30b71afca3309787e086df5a1729cb2a96 Mon Sep 17 00:00:00 2001 From: Yousong Zhou Date: Mon, 16 Dec 2019 17:46:57 +0000 Subject: [PATCH 11/24] hosts: GetNetworkWithId: take into account onecloud vpc subnet --- pkg/compute/models/hosts.go | 35 +++++++++++++++++++++++++++-------- 1 file changed, 27 insertions(+), 8 deletions(-) diff --git a/pkg/compute/models/hosts.go b/pkg/compute/models/hosts.go index 2b2fd50b32..f9dd3097ab 100644 --- a/pkg/compute/models/hosts.go +++ b/pkg/compute/models/hosts.go @@ -1984,15 +1984,34 @@ func (self *SHost) GetNetinterfacesWithIdAndCredential(netId string, userCred mc } func (self *SHost) GetNetworkWithId(netId string, reserved bool) (*SNetwork, error) { - networks := NetworkManager.Query().SubQuery() - hostwires := HostwireManager.Query().SubQuery() - hosts := HostManager.Query().SubQuery() + 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) From 54783f601a12fc8aadb7d31bec1f5cb25d1f3689 Mon Sep 17 00:00:00 2001 From: Yousong Zhou Date: Fri, 20 Dec 2019 21:42:38 +0800 Subject: [PATCH 12/24] hosts: Add OvnVersion field --- pkg/compute/models/hosts.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pkg/compute/models/hosts.go b/pkg/compute/models/hosts.go index f9dd3097ab..6151b68731 100644 --- a/pkg/compute/models/hosts.go +++ b/pkg/compute/models/hosts.go @@ -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) From 4221e214f015e722bd747c5994e1e1afd83889bb Mon Sep 17 00:00:00 2001 From: Yousong Zhou Date: Mon, 16 Dec 2019 09:01:01 +0000 Subject: [PATCH 13/24] guestnetworks: nic desc for onecloud vpc --- pkg/apis/compute/vpcs_const.go | 4 ++++ pkg/compute/models/guestnetworks.go | 35 +++++++++++++++++++++-------- 2 files changed, 30 insertions(+), 9 deletions(-) diff --git a/pkg/apis/compute/vpcs_const.go b/pkg/apis/compute/vpcs_const.go index 4f71006ed7..e8d5538845 100644 --- a/pkg/apis/compute/vpcs_const.go +++ b/pkg/apis/compute/vpcs_const.go @@ -70,3 +70,7 @@ type WireListInput struct { type GlobalVpcListInput struct { apis.EnabledStatusStandaloneResourceListInput } + +const ( + VPC_PROVIDER_OVN = "ovn" +) diff --git a/pkg/compute/models/guestnetworks.go b/pkg/compute/models/guestnetworks.go index 0520c3acee..d51bac0b22 100644 --- a/pkg/compute/models/guestnetworks.go +++ b/pkg/compute/models/guestnetworks.go @@ -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") From 770a6d194d644fa4432eeb1695b88bcd14c247ca Mon Sep 17 00:00:00 2001 From: Yousong Zhou Date: Fri, 13 Dec 2019 10:05:29 +0000 Subject: [PATCH 14/24] cloudcommon: extend interface for fetching metadata --- pkg/cloudcommon/db/interface.go | 4 ++++ pkg/cloudcommon/db/modelbase.go | 12 ++++++++++++ pkg/cloudcommon/db/opslog.go | 8 ++++++++ pkg/cloudcommon/db/resourcebase.go | 12 ++++++++++++ 4 files changed, 36 insertions(+) diff --git a/pkg/cloudcommon/db/interface.go b/pkg/cloudcommon/db/interface.go index 6283bf8c62..6b1868e725 100644 --- a/pkg/cloudcommon/db/interface.go +++ b/pkg/cloudcommon/db/interface.go @@ -129,6 +129,9 @@ type IModel interface { object.IObject GetName() string + GetUpdateVersion() int + GetUpdatedAt() time.Time + GetDeleted() bool KeywordPlural() string @@ -283,6 +286,7 @@ type IVirtualModelManager interface { type IVirtualModel interface { IStandaloneModel + IPendingDeletable IsOwner(userCred mcclient.TokenCredential) bool // IsAdmin(userCred mcclient.TokenCredential) bool diff --git a/pkg/cloudcommon/db/modelbase.go b/pkg/cloudcommon/db/modelbase.go index 3a2485b6d2..06dd0c1242 100644 --- a/pkg/cloudcommon/db/modelbase.go +++ b/pkg/cloudcommon/db/modelbase.go @@ -412,6 +412,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) diff --git a/pkg/cloudcommon/db/opslog.go b/pkg/cloudcommon/db/opslog.go index 582ec58f59..80ee3977fb 100644 --- a/pkg/cloudcommon/db/opslog.go +++ b/pkg/cloudcommon/db/opslog.go @@ -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 } diff --git a/pkg/cloudcommon/db/resourcebase.go b/pkg/cloudcommon/db/resourcebase.go index 903cc5503c..e7c39dfc6f 100644 --- a/pkg/cloudcommon/db/resourcebase.go +++ b/pkg/cloudcommon/db/resourcebase.go @@ -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 +} From 3cd804922231ffb158ae9dff0ed7a6454d37cbee Mon Sep 17 00:00:00 2001 From: Yousong Zhou Date: Sat, 7 Dec 2019 10:04:47 +0000 Subject: [PATCH 15/24] cloudcommon: db: allow sysadmin to list deleted resources The query arg name cannot be "deleted" to avoid collide with resource field name. --- pkg/cloudcommon/db/db_dispatcher.go | 45 ++++++++++++++++++++++++----- pkg/cloudcommon/db/interface.go | 1 + pkg/cloudcommon/db/modelbase.go | 4 +++ pkg/cloudcommon/db/resourcebase.go | 2 +- 4 files changed, 44 insertions(+), 8 deletions(-) diff --git a/pkg/cloudcommon/db/db_dispatcher.go b/pkg/cloudcommon/db/db_dispatcher.go index 5ac87f5718..b073171c56 100644 --- a/pkg/cloudcommon/db/db_dispatcher.go +++ b/pkg/cloudcommon/db/db_dispatcher.go @@ -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 } diff --git a/pkg/cloudcommon/db/interface.go b/pkg/cloudcommon/db/interface.go index 6b1868e725..308afbd97c 100644 --- a/pkg/cloudcommon/db/interface.go +++ b/pkg/cloudcommon/db/interface.go @@ -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 diff --git a/pkg/cloudcommon/db/modelbase.go b/pkg/cloudcommon/db/modelbase.go index 06dd0c1242..a4dcf595ca 100644 --- a/pkg/cloudcommon/db/modelbase.go +++ b/pkg/cloudcommon/db/modelbase.go @@ -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 } diff --git a/pkg/cloudcommon/db/resourcebase.go b/pkg/cloudcommon/db/resourcebase.go index e7c39dfc6f..cf9a22bdf3 100644 --- a/pkg/cloudcommon/db/resourcebase.go +++ b/pkg/cloudcommon/db/resourcebase.go @@ -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 { From 5226deb36642abf1ba909e970ab483cdd7cf13d8 Mon Sep 17 00:00:00 2001 From: Yousong Zhou Date: Sat, 7 Dec 2019 10:50:47 +0000 Subject: [PATCH 16/24] mcclient: options: list all including deleted resource with --delete-all --- pkg/mcclient/options/base.go | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/pkg/mcclient/options/base.go b/pkg/mcclient/options/base.go index e6a7d478b1..54abc5d23d 100644 --- a/pkg/mcclient/options/base.go +++ b/pkg/mcclient/options/base.go @@ -199,8 +199,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:"" json:"-"` @@ -248,6 +249,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 From 9173e8aa0bcfe8b5660a319a4d4812fd9a4e97f2 Mon Sep 17 00:00:00 2001 From: Yousong Zhou Date: Sun, 15 Dec 2019 06:24:51 +0000 Subject: [PATCH 17/24] hostman: fix wording: detective -> detect --- pkg/hostman/hostinfo/hostinfo.go | 26 +++++++++++++------------- pkg/util/sysutils/kvm.go | 4 ++-- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/pkg/hostman/hostinfo/hostinfo.go b/pkg/hostman/hostinfo/hostinfo.go index 4a04c32571..50ed8e6700 100644 --- a/pkg/hostman/hostinfo/hostinfo.go +++ b/pkg/hostman/hostinfo/hostinfo.go @@ -327,11 +327,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 +353,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 +483,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 +503,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 +517,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 +547,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) diff --git a/pkg/util/sysutils/kvm.go b/pkg/util/sysutils/kvm.go index 29680cf367..d89e3209a2 100644 --- a/pkg/util/sysutils/kvm.go +++ b/pkg/util/sysutils/kvm.go @@ -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 From 496bbbf5c055dfd2e309c13d5c053971f0503aa6 Mon Sep 17 00:00:00 2001 From: Yousong Zhou Date: Sun, 15 Dec 2019 06:56:44 +0000 Subject: [PATCH 18/24] hostman: hostbridge: const values for driver types --- pkg/hostman/hostinfo/hostbridge/const.go | 20 +++++++++++++++++++ pkg/hostman/hostinfo/hostbridge/hostbridge.go | 12 +++++------ 2 files changed, 26 insertions(+), 6 deletions(-) create mode 100644 pkg/hostman/hostinfo/hostbridge/const.go diff --git a/pkg/hostman/hostinfo/hostbridge/const.go b/pkg/hostman/hostinfo/hostbridge/const.go new file mode 100644 index 0000000000..843be166a3 --- /dev/null +++ b/pkg/hostman/hostinfo/hostbridge/const.go @@ -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" +) diff --git a/pkg/hostman/hostinfo/hostbridge/hostbridge.go b/pkg/hostman/hostinfo/hostbridge/hostbridge.go index b19902d356..ee27f03a9f 100644 --- a/pkg/hostman/hostinfo/hostbridge/hostbridge.go +++ b/pkg/hostman/hostinfo/hostbridge/hostbridge.go @@ -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() } } From 2a6f76539cf7406c2250707e886572cd5750e90b Mon Sep 17 00:00:00 2001 From: Yousong Zhou Date: Fri, 3 Jan 2020 14:11:14 +0800 Subject: [PATCH 19/24] hostman: drop flow mods in ifup/ifdown script For classic network iface, these are to be handled by sdnagent. However these can cause problem for ovn-controller --- pkg/hostman/hostinfo/hostbridge/ovs.go | 87 -------------------------- 1 file changed, 87 deletions(-) diff --git a/pkg/hostman/hostinfo/hostbridge/ovs.go b/pkg/hostman/hostinfo/hostbridge/ovs.go index c5958e9841..a5c45cafe7 100644 --- a/pkg/hostman/hostinfo/hostbridge/ovs.go +++ b/pkg/hostman/hostinfo/hostbridge/ovs.go @@ -25,7 +25,6 @@ import ( "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" ) @@ -161,9 +160,6 @@ func (o *SOVSBridgeDriver) getUpScripts(nic jsonutils.JSONObject) (string, error 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 +193,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 +204,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 } From 99e2a1a948dc7db90158e37044b717d3dd2f131e Mon Sep 17 00:00:00 2001 From: Yousong Zhou Date: Thu, 12 Dec 2019 16:07:17 +0000 Subject: [PATCH 20/24] hostman: add OvnHelper --- pkg/hostman/guestman/qemu-kvm.go | 12 ++ pkg/hostman/hostinfo/hostbridge/ovs.go | 25 ++- pkg/hostman/hostinfo/hostinfo.go | 25 +++ pkg/hostman/hostinfo/hostovn.go | 159 +++++++++++++++++++ pkg/hostman/hostinfo/hostovn_test.go | 67 ++++++++ pkg/hostman/options/options.go | 4 + pkg/hostman/system_service/ovn_conroller.go | 33 ++++ pkg/hostman/system_service/system_service.go | 19 +-- 8 files changed, 330 insertions(+), 14 deletions(-) create mode 100644 pkg/hostman/hostinfo/hostovn.go create mode 100644 pkg/hostman/hostinfo/hostovn_test.go create mode 100644 pkg/hostman/system_service/ovn_conroller.go diff --git a/pkg/hostman/guestman/qemu-kvm.go b/pkg/hostman/guestman/qemu-kvm.go index a14249813a..84c9cfd330 100644 --- a/pkg/hostman/guestman/qemu-kvm.go +++ b/pkg/hostman/guestman/qemu-kvm.go @@ -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) } diff --git a/pkg/hostman/hostinfo/hostbridge/ovs.go b/pkg/hostman/hostinfo/hostbridge/ovs.go index a5c45cafe7..50baa9ddd1 100644 --- a/pkg/hostman/hostinfo/hostbridge/ovs.go +++ b/pkg/hostman/hostinfo/hostbridge/ovs.go @@ -22,6 +22,7 @@ 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" @@ -116,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 @@ -154,6 +162,9 @@ 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" @@ -264,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, "", "") +} diff --git a/pkg/hostman/hostinfo/hostinfo.go b/pkg/hostman/hostinfo/hostinfo.go index 50ed8e6700..4b5d5b4e36 100644 --- a/pkg/hostman/hostinfo/hostinfo.go +++ b/pkg/hostman/hostinfo/hostinfo.go @@ -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 { @@ -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 diff --git a/pkg/hostman/hostinfo/hostovn.go b/pkg/hostman/hostinfo/hostovn.go new file mode 100644 index 0000000000..a3e2d5c843 --- /dev/null +++ b/pkg/hostman/hostinfo/hostovn.go @@ -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 "" +} diff --git a/pkg/hostman/hostinfo/hostovn_test.go b/pkg/hostman/hostinfo/hostovn_test.go new file mode 100644 index 0000000000..7929824742 --- /dev/null +++ b/pkg/hostman/hostinfo/hostovn_test.go @@ -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) + } + } +} diff --git a/pkg/hostman/options/options.go b/pkg/hostman/options/options.go index 802d2df4fd..e5f6e7394e 100644 --- a/pkg/hostman/options/options.go +++ b/pkg/hostman/options/options.go @@ -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 diff --git a/pkg/hostman/system_service/ovn_conroller.go b/pkg/hostman/system_service/ovn_conroller.go new file mode 100644 index 0000000000..a52e962967 --- /dev/null +++ b/pkg/hostman/system_service/ovn_conroller.go @@ -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()) +} diff --git a/pkg/hostman/system_service/system_service.go b/pkg/hostman/system_service/system_service.go index df422f8983..4978209402 100644 --- a/pkg/hostman/system_service/system_service.go +++ b/pkg/hostman/system_service/system_service.go @@ -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(), } } From da68e7fccf52fcbe2a8aa8d84bbed29fbbb05e0c Mon Sep 17 00:00:00 2001 From: Yousong Zhou Date: Mon, 16 Dec 2019 15:46:23 +0000 Subject: [PATCH 21/24] scheduler: simplify findCandidatesThatFit() --- pkg/scheduler/core/generic_scheduler.go | 27 +++++++++---------------- 1 file changed, 10 insertions(+), 17 deletions(-) diff --git a/pkg/scheduler/core/generic_scheduler.go b/pkg/scheduler/core/generic_scheduler.go index 79b48f4845..1db1247d19 100644 --- a/pkg/scheduler/core/generic_scheduler.go +++ b/pkg/scheduler/core/generic_scheduler.go @@ -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 { From d17452240b718f24087dc61e1c1e3f3d26a524c8 Mon Sep 17 00:00:00 2001 From: Yousong Zhou Date: Thu, 9 Jan 2020 20:54:07 +0800 Subject: [PATCH 22/24] scheduler: simplify MinCounters --- pkg/scheduler/core/context.go | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/pkg/scheduler/core/context.go b/pkg/scheduler/core/context.go index 383c9a26ea..bcae3f2382 100644 --- a/pkg/scheduler/core/context.go +++ b/pkg/scheduler/core/context.go @@ -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 From 7c29b06669912d30c01edaf0b81339f9d11904e8 Mon Sep 17 00:00:00 2001 From: Yousong Zhou Date: Mon, 16 Dec 2019 16:37:41 +0000 Subject: [PATCH 23/24] scheduler: network_predicate: allow onecloud vpc networks --- .../algorithm/predicates/network_predicate.go | 52 +++++++++++++------ .../predicates/network_schedtag_predicate.go | 6 +++ pkg/scheduler/api/types.go | 3 ++ pkg/scheduler/cache/candidate/base.go | 47 +++++++++++++++++ pkg/scheduler/cache/candidate/hosts.go | 4 ++ pkg/scheduler/core/types.go | 1 + 6 files changed, 97 insertions(+), 16 deletions(-) diff --git a/pkg/scheduler/algorithm/predicates/network_predicate.go b/pkg/scheduler/algorithm/predicates/network_predicate.go index d3a07f371e..9eaa31a566 100644 --- a/pkg/scheduler/algorithm/predicates/network_predicate.go +++ b/pkg/scheduler/algorithm/predicates/network_predicate.go @@ -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 { diff --git a/pkg/scheduler/algorithm/predicates/network_schedtag_predicate.go b/pkg/scheduler/algorithm/predicates/network_schedtag_predicate.go index 26954eaaab..56274b7a1a 100644 --- a/pkg/scheduler/algorithm/predicates/network_schedtag_predicate.go +++ b/pkg/scheduler/algorithm/predicates/network_schedtag_predicate.go @@ -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{ diff --git a/pkg/scheduler/api/types.go b/pkg/scheduler/api/types.go index d7d25e2015..db2fbfe151 100644 --- a/pkg/scheduler/api/types.go +++ b/pkg/scheduler/api/types.go @@ -101,6 +101,9 @@ type CandidateStorage struct { type CandidateNetwork struct { *models.SNetwork Schedtags []models.SSchedtag `json:"schedtags"` + + Provider string + VpcId string } type CandidateGroup struct { diff --git a/pkg/scheduler/cache/candidate/base.go b/pkg/scheduler/cache/candidate/base.go index cb0f0587be..16ca3c9a83 100644 --- a/pkg/scheduler/cache/candidate/base.go +++ b/pkg/scheduler/cache/candidate/base.go @@ -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() { diff --git a/pkg/scheduler/cache/candidate/hosts.go b/pkg/scheduler/cache/candidate/hosts.go index 9f9ddbc16c..b30bc31b26 100644 --- a/pkg/scheduler/cache/candidate/hosts.go +++ b/pkg/scheduler/cache/candidate/hosts.go @@ -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 diff --git a/pkg/scheduler/core/types.go b/pkg/scheduler/core/types.go index 09a6c7502e..0213372283 100644 --- a/pkg/scheduler/core/types.go +++ b/pkg/scheduler/core/types.go @@ -66,6 +66,7 @@ type CandidatePropertyGetter interface { HostSchedtags() []computemodels.SSchedtag Storages() []*api.CandidateStorage Networks() []*api.CandidateNetwork + OvnCapable() bool Status() string HostStatus() string Enabled() bool From bc999888a2e05aa2c2b39836a3b3e2805dbaf356 Mon Sep 17 00:00:00 2001 From: Yousong Zhou Date: Wed, 4 Dec 2019 16:43:37 +0000 Subject: [PATCH 24/24] vpcagent: a vpc agent with ovn driver --- cmd/vpcagent/main.go | 71 + pkg/vpcagent/apihelper/apihelper.go | 129 ++ pkg/vpcagent/apihelper/doc.go | 15 + pkg/vpcagent/apihelper/interface.go | 60 + pkg/vpcagent/apihelper/reflect.go | 264 +++ pkg/vpcagent/models/doc.go | 15 + pkg/vpcagent/models/models.go | 59 + pkg/vpcagent/models/modelset.go | 147 ++ pkg/vpcagent/models/modelsets.go | 115 + pkg/vpcagent/options/doc.go | 15 + pkg/vpcagent/options/options.go | 68 + pkg/vpcagent/ovn/doc.go | 15 + pkg/vpcagent/ovn/keeper.go | 277 +++ pkg/vpcagent/ovn/register.go | 27 + pkg/vpcagent/ovn/worker.go | 121 ++ pkg/vpcagent/ovnutil/atomics_ensure.go | 774 +++++++ pkg/vpcagent/ovnutil/atomics_match.go | 990 +++++++++ pkg/vpcagent/ovnutil/atomics_ovn_args.go | 425 ++++ pkg/vpcagent/ovnutil/doc.go | 15 + pkg/vpcagent/ovnutil/ovn_nbctl.go | 167 ++ pkg/vpcagent/ovnutil/schema.go | 2440 ++++++++++++++++++++++ pkg/vpcagent/ovnutil/schema_test.go | 47 + pkg/vpcagent/ovnutil/unmarshal.go | 61 + pkg/vpcagent/worker/doc.go | 15 + pkg/vpcagent/worker/worker.go | 45 + 25 files changed, 6377 insertions(+) create mode 100644 cmd/vpcagent/main.go create mode 100644 pkg/vpcagent/apihelper/apihelper.go create mode 100644 pkg/vpcagent/apihelper/doc.go create mode 100644 pkg/vpcagent/apihelper/interface.go create mode 100644 pkg/vpcagent/apihelper/reflect.go create mode 100644 pkg/vpcagent/models/doc.go create mode 100644 pkg/vpcagent/models/models.go create mode 100644 pkg/vpcagent/models/modelset.go create mode 100644 pkg/vpcagent/models/modelsets.go create mode 100644 pkg/vpcagent/options/doc.go create mode 100644 pkg/vpcagent/options/options.go create mode 100644 pkg/vpcagent/ovn/doc.go create mode 100644 pkg/vpcagent/ovn/keeper.go create mode 100644 pkg/vpcagent/ovn/register.go create mode 100644 pkg/vpcagent/ovn/worker.go create mode 100644 pkg/vpcagent/ovnutil/atomics_ensure.go create mode 100644 pkg/vpcagent/ovnutil/atomics_match.go create mode 100644 pkg/vpcagent/ovnutil/atomics_ovn_args.go create mode 100644 pkg/vpcagent/ovnutil/doc.go create mode 100644 pkg/vpcagent/ovnutil/ovn_nbctl.go create mode 100644 pkg/vpcagent/ovnutil/schema.go create mode 100644 pkg/vpcagent/ovnutil/schema_test.go create mode 100644 pkg/vpcagent/ovnutil/unmarshal.go create mode 100644 pkg/vpcagent/worker/doc.go create mode 100644 pkg/vpcagent/worker/worker.go diff --git a/cmd/vpcagent/main.go b/cmd/vpcagent/main.go new file mode 100644 index 0000000000..fb3d69bc43 --- /dev/null +++ b/cmd/vpcagent/main.go @@ -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() + } +} diff --git a/pkg/vpcagent/apihelper/apihelper.go b/pkg/vpcagent/apihelper/apihelper.go new file mode 100644 index 0000000000..e5ca576b78 --- /dev/null +++ b/pkg/vpcagent/apihelper/apihelper.go @@ -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 +} diff --git a/pkg/vpcagent/apihelper/doc.go b/pkg/vpcagent/apihelper/doc.go new file mode 100644 index 0000000000..2d3136b78a --- /dev/null +++ b/pkg/vpcagent/apihelper/doc.go @@ -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" diff --git a/pkg/vpcagent/apihelper/interface.go b/pkg/vpcagent/apihelper/interface.go new file mode 100644 index 0000000000..90959c4ff1 --- /dev/null +++ b/pkg/vpcagent/apihelper/interface.go @@ -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 +} diff --git a/pkg/vpcagent/apihelper/reflect.go b/pkg/vpcagent/apihelper/reflect.go new file mode 100644 index 0000000000..86c1939396 --- /dev/null +++ b/pkg/vpcagent/apihelper/reflect.go @@ -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 +} diff --git a/pkg/vpcagent/models/doc.go b/pkg/vpcagent/models/doc.go new file mode 100644 index 0000000000..4be3a71bc7 --- /dev/null +++ b/pkg/vpcagent/models/doc.go @@ -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" diff --git a/pkg/vpcagent/models/models.go b/pkg/vpcagent/models/models.go new file mode 100644 index 0000000000..df0035c291 --- /dev/null +++ b/pkg/vpcagent/models/models.go @@ -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, + } +} diff --git a/pkg/vpcagent/models/modelset.go b/pkg/vpcagent/models/modelset.go new file mode 100644 index 0000000000..b60c9c255a --- /dev/null +++ b/pkg/vpcagent/models/modelset.go @@ -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 +} diff --git a/pkg/vpcagent/models/modelsets.go b/pkg/vpcagent/models/modelsets.go new file mode 100644 index 0000000000..e5cc86b678 --- /dev/null +++ b/pkg/vpcagent/models/modelsets.go @@ -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 +} diff --git a/pkg/vpcagent/options/doc.go b/pkg/vpcagent/options/doc.go new file mode 100644 index 0000000000..1c7dd93ab6 --- /dev/null +++ b/pkg/vpcagent/options/doc.go @@ -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" diff --git a/pkg/vpcagent/options/options.go b/pkg/vpcagent/options/options.go new file mode 100644 index 0000000000..17a96fbc91 --- /dev/null +++ b/pkg/vpcagent/options/options.go @@ -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 +} diff --git a/pkg/vpcagent/ovn/doc.go b/pkg/vpcagent/ovn/doc.go new file mode 100644 index 0000000000..24246d8834 --- /dev/null +++ b/pkg/vpcagent/ovn/doc.go @@ -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" diff --git a/pkg/vpcagent/ovn/keeper.go b/pkg/vpcagent/ovn/keeper.go new file mode 100644 index 0000000000..521354ed20 --- /dev/null +++ b/pkg/vpcagent/ovn/keeper.go @@ -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", ""} + 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 +} diff --git a/pkg/vpcagent/ovn/register.go b/pkg/vpcagent/ovn/register.go new file mode 100644 index 0000000000..96d096932e --- /dev/null +++ b/pkg/vpcagent/ovn/register.go @@ -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, + ) +} diff --git a/pkg/vpcagent/ovn/worker.go b/pkg/vpcagent/ovn/worker.go new file mode 100644 index 0000000000..8f9a7c3869 --- /dev/null +++ b/pkg/vpcagent/ovn/worker.go @@ -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 +} diff --git a/pkg/vpcagent/ovnutil/atomics_ensure.go b/pkg/vpcagent/ovnutil/atomics_ensure.go new file mode 100644 index 0000000000..966eb7fa93 --- /dev/null +++ b/pkg/vpcagent/ovnutil/atomics_ensure.go @@ -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 +} diff --git a/pkg/vpcagent/ovnutil/atomics_match.go b/pkg/vpcagent/ovnutil/atomics_match.go new file mode 100644 index 0000000000..13b6530e2a --- /dev/null +++ b/pkg/vpcagent/ovnutil/atomics_match.go @@ -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 +} diff --git a/pkg/vpcagent/ovnutil/atomics_ovn_args.go b/pkg/vpcagent/ovnutil/atomics_ovn_args.go new file mode 100644 index 0000000000..d5f17221a5 --- /dev/null +++ b/pkg/vpcagent/ovnutil/atomics_ovn_args.go @@ -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 +} diff --git a/pkg/vpcagent/ovnutil/doc.go b/pkg/vpcagent/ovnutil/doc.go new file mode 100644 index 0000000000..494928eac4 --- /dev/null +++ b/pkg/vpcagent/ovnutil/doc.go @@ -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" diff --git a/pkg/vpcagent/ovnutil/ovn_nbctl.go b/pkg/vpcagent/ovnutil/ovn_nbctl.go new file mode 100644 index 0000000000..db63639c0e --- /dev/null +++ b/pkg/vpcagent/ovnutil/ovn_nbctl.go @@ -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 +} diff --git a/pkg/vpcagent/ovnutil/schema.go b/pkg/vpcagent/ovnutil/schema.go new file mode 100644 index 0000000000..f9f97a0f1d --- /dev/null +++ b/pkg/vpcagent/ovnutil/schema.go @@ -0,0 +1,2440 @@ +// 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" + + "yunion.io/x/pkg/errors" +) + +const ( + ErrUnknownColumn = errors.Error("unknown column") + ErrUnknownTable = errors.Error("unknown table") +) + +type IRow interface { + SetColumn(name string, val interface{}) error + OvnTableName() string + OvnIsRoot() bool + OvnUuid() string + OvnArgs() []string + OvnSetExternalIds(k, v string) + OvnGetExternalIds(k string) (string, bool) + OvnRemoveExternalIds(k string) (string, bool) +} + +type ITable interface { + NewRow() IRow + Rows() []IRow + OvnTableName() string + OvnIsRoot() bool +} + +type OVNNorthbound struct { + SSL SSLTable + NBGlobal NBGlobalTable + LogicalRouterStaticRoute LogicalRouterStaticRouteTable + LoadBalancer LoadBalancerTable + Connection ConnectionTable + DNS DNSTable + QoS QoSTable + LogicalRouter LogicalRouterTable + LogicalSwitchPort LogicalSwitchPortTable + GatewayChassis GatewayChassisTable + LogicalRouterPort LogicalRouterPortTable + LogicalSwitch LogicalSwitchTable + NAT NATTable + DHCPOptions DHCPOptionsTable + ACL ACLTable + AddressSet AddressSetTable +} + +func (db *OVNNorthbound) FindOneMatchNonZeros(irow IRow) (r IRow) { + switch row := irow.(type) { + case *GatewayChassis: + if r := db.GatewayChassis.FindOneMatchNonZeros(row); r != nil { + return r + } + return nil + case *LogicalRouterPort: + if r := db.LogicalRouterPort.FindOneMatchNonZeros(row); r != nil { + return r + } + return nil + case *LogicalSwitch: + if r := db.LogicalSwitch.FindOneMatchNonZeros(row); r != nil { + return r + } + return nil + case *NAT: + if r := db.NAT.FindOneMatchNonZeros(row); r != nil { + return r + } + return nil + case *DHCPOptions: + if r := db.DHCPOptions.FindOneMatchNonZeros(row); r != nil { + return r + } + return nil + case *ACL: + if r := db.ACL.FindOneMatchNonZeros(row); r != nil { + return r + } + return nil + case *AddressSet: + if r := db.AddressSet.FindOneMatchNonZeros(row); r != nil { + return r + } + return nil + case *SSL: + if r := db.SSL.FindOneMatchNonZeros(row); r != nil { + return r + } + return nil + case *NBGlobal: + if r := db.NBGlobal.FindOneMatchNonZeros(row); r != nil { + return r + } + return nil + case *LogicalRouterStaticRoute: + if r := db.LogicalRouterStaticRoute.FindOneMatchNonZeros(row); r != nil { + return r + } + return nil + case *LoadBalancer: + if r := db.LoadBalancer.FindOneMatchNonZeros(row); r != nil { + return r + } + return nil + case *Connection: + if r := db.Connection.FindOneMatchNonZeros(row); r != nil { + return r + } + return nil + case *DNS: + if r := db.DNS.FindOneMatchNonZeros(row); r != nil { + return r + } + return nil + case *QoS: + if r := db.QoS.FindOneMatchNonZeros(row); r != nil { + return r + } + return nil + case *LogicalRouter: + if r := db.LogicalRouter.FindOneMatchNonZeros(row); r != nil { + return r + } + return nil + case *LogicalSwitchPort: + if r := db.LogicalSwitchPort.FindOneMatchNonZeros(row); r != nil { + return r + } + return nil + } + panic(ErrUnknownTable) +} + +type LogicalRouter struct { + Uuid string `json:"-"` + Enabled *bool `json:"enabled"` + Nat []string `json:"nat"` + LoadBalancer []string `json:"load_balancer"` + Options map[string]string `json:"options"` + ExternalIds map[string]string `json:"external_ids"` + Name string `json:"name"` + Ports []string `json:"ports"` + StaticRoutes []string `json:"static_routes"` +} + +func (row *LogicalRouter) OvnTableName() string { + return "Logical_Router" +} + +func (row *LogicalRouter) OvnIsRoot() bool { + return true +} + +func (row *LogicalRouter) OvnUuid() string { + return row.Uuid +} + +func (row *LogicalRouter) OvnSetExternalIds(k, v string) { + if row.ExternalIds == nil { + row.ExternalIds = map[string]string{} + } + row.ExternalIds[k] = v +} + +func (row *LogicalRouter) OvnGetExternalIds(k string) (string, bool) { + if row.ExternalIds == nil { + return "", false + } + r, ok := row.ExternalIds[k] + return r, ok +} + +func (row *LogicalRouter) OvnRemoveExternalIds(k string) (string, bool) { + if row.ExternalIds == nil { + return "", false + } + r, ok := row.ExternalIds[k] + if ok { + delete(row.ExternalIds, k) + } + return r, ok +} + +func (row *LogicalRouter) SetColumn(name string, val interface{}) (err error) { + defer func() { + if panicErr := recover(); panicErr != nil { + err = errors.Wrapf(panicErr.(error), "%s: %#v", name, fmt.Sprintf("%#v", val)) + } + }() + switch name { + case "_uuid": + row.Uuid = ensureUuid(val) + case "nat": + row.Nat = ensureUuidMultiples(val) + case "load_balancer": + row.LoadBalancer = ensureUuidMultiples(val) + case "options": + row.Options = ensureMapStringString(val) + case "external_ids": + row.ExternalIds = ensureMapStringString(val) + case "name": + row.Name = ensureString(val) + case "ports": + row.Ports = ensureUuidMultiples(val) + case "static_routes": + row.StaticRoutes = ensureUuidMultiples(val) + case "enabled": + row.Enabled = ensureBooleanOptional(val) + default: + panic(ErrUnknownColumn) + } + return +} + +func (row *LogicalRouter) MatchNonZeros(row1 *LogicalRouter) bool { + if !matchBooleanOptionalIfNonZero(row.Enabled, row1.Enabled) { + return false + } + if !matchUuidMultiplesIfNonZero(row.Nat, row1.Nat) { + return false + } + if !matchUuidMultiplesIfNonZero(row.LoadBalancer, row1.LoadBalancer) { + return false + } + if !matchMapStringStringIfNonZero(row.Options, row1.Options) { + return false + } + if !matchMapStringStringIfNonZero(row.ExternalIds, row1.ExternalIds) { + return false + } + if !matchStringIfNonZero(row.Name, row1.Name) { + return false + } + if !matchUuidMultiplesIfNonZero(row.Ports, row1.Ports) { + return false + } + if !matchUuidMultiplesIfNonZero(row.StaticRoutes, row1.StaticRoutes) { + return false + } + return true +} + +func (row *LogicalRouter) OvnArgs() []string { + r := []string{} + r = append(r, OvnArgsUuidMultiples("nat", row.Nat)...) + r = append(r, OvnArgsUuidMultiples("load_balancer", row.LoadBalancer)...) + r = append(r, OvnArgsMapStringString("options", row.Options)...) + r = append(r, OvnArgsMapStringString("external_ids", row.ExternalIds)...) + r = append(r, OvnArgsString("name", row.Name)...) + r = append(r, OvnArgsUuidMultiples("ports", row.Ports)...) + r = append(r, OvnArgsUuidMultiples("static_routes", row.StaticRoutes)...) + r = append(r, OvnArgsBooleanOptional("enabled", row.Enabled)...) + return r +} + +type LogicalRouterTable []LogicalRouter + +func (tbl *LogicalRouterTable) NewRow() IRow { + *tbl = append(*tbl, LogicalRouter{}) + return &(*tbl)[len(*tbl)-1] +} + +func (tbl LogicalRouterTable) OvnTableName() string { + return "Logical_Router" +} + +func (tbl LogicalRouterTable) OvnIsRoot() bool { + return true +} + +func (tbl LogicalRouterTable) Rows() []IRow { + r := make([]IRow, len(tbl)) + for i := range tbl { + r[i] = &tbl[i] + } + return r +} + +func (tbl LogicalRouterTable) FindOneMatchNonZeros(row1 *LogicalRouter) *LogicalRouter { + for i := range tbl { + row := &tbl[i] + if row.MatchNonZeros(row1) { + return row + } + } + return nil +} + +type LogicalSwitchPort struct { + Uuid string `json:"-"` + PortSecurity []string `json:"port_security"` + Type string `json:"type"` + Dhcpv4Options *string `json:"dhcpv4_options"` + Options map[string]string `json:"options"` + Dhcpv6Options *string `json:"dhcpv6_options"` + ParentName *string `json:"parent_name"` + Enabled *bool `json:"enabled"` + ExternalIds map[string]string `json:"external_ids"` + TagRequest *int64 `json:"tag_request"` + Addresses []string `json:"addresses"` + DynamicAddresses *string `json:"dynamic_addresses"` + Name string `json:"name"` + Tag *int64 `json:"tag"` + Up *bool `json:"up"` +} + +func (row *LogicalSwitchPort) OvnTableName() string { + return "Logical_Switch_Port" +} + +func (row *LogicalSwitchPort) OvnIsRoot() bool { + return false +} + +func (row *LogicalSwitchPort) OvnUuid() string { + return row.Uuid +} + +func (row *LogicalSwitchPort) OvnSetExternalIds(k, v string) { + if row.ExternalIds == nil { + row.ExternalIds = map[string]string{} + } + row.ExternalIds[k] = v +} + +func (row *LogicalSwitchPort) OvnGetExternalIds(k string) (string, bool) { + if row.ExternalIds == nil { + return "", false + } + r, ok := row.ExternalIds[k] + return r, ok +} + +func (row *LogicalSwitchPort) OvnRemoveExternalIds(k string) (string, bool) { + if row.ExternalIds == nil { + return "", false + } + r, ok := row.ExternalIds[k] + if ok { + delete(row.ExternalIds, k) + } + return r, ok +} + +func (row *LogicalSwitchPort) SetColumn(name string, val interface{}) (err error) { + defer func() { + if panicErr := recover(); panicErr != nil { + err = errors.Wrapf(panicErr.(error), "%s: %#v", name, fmt.Sprintf("%#v", val)) + } + }() + switch name { + case "_uuid": + row.Uuid = ensureUuid(val) + case "port_security": + row.PortSecurity = ensureStringMultiples(val) + case "type": + row.Type = ensureString(val) + case "dhcpv4_options": + row.Dhcpv4Options = ensureUuidOptional(val) + case "options": + row.Options = ensureMapStringString(val) + case "dhcpv6_options": + row.Dhcpv6Options = ensureUuidOptional(val) + case "parent_name": + row.ParentName = ensureStringOptional(val) + case "enabled": + row.Enabled = ensureBooleanOptional(val) + case "name": + row.Name = ensureString(val) + case "tag": + row.Tag = ensureIntegerOptional(val) + case "up": + row.Up = ensureBooleanOptional(val) + case "external_ids": + row.ExternalIds = ensureMapStringString(val) + case "tag_request": + row.TagRequest = ensureIntegerOptional(val) + case "addresses": + row.Addresses = ensureStringMultiples(val) + case "dynamic_addresses": + row.DynamicAddresses = ensureStringOptional(val) + default: + panic(ErrUnknownColumn) + } + return +} + +func (row *LogicalSwitchPort) MatchNonZeros(row1 *LogicalSwitchPort) bool { + if !matchStringMultiplesIfNonZero(row.Addresses, row1.Addresses) { + return false + } + if !matchStringOptionalIfNonZero(row.DynamicAddresses, row1.DynamicAddresses) { + return false + } + if !matchStringIfNonZero(row.Name, row1.Name) { + return false + } + if !matchIntegerOptionalIfNonZero(row.Tag, row1.Tag) { + return false + } + if !matchBooleanOptionalIfNonZero(row.Up, row1.Up) { + return false + } + if !matchMapStringStringIfNonZero(row.ExternalIds, row1.ExternalIds) { + return false + } + if !matchIntegerOptionalIfNonZero(row.TagRequest, row1.TagRequest) { + return false + } + if !matchUuidOptionalIfNonZero(row.Dhcpv4Options, row1.Dhcpv4Options) { + return false + } + if !matchStringMultiplesIfNonZero(row.PortSecurity, row1.PortSecurity) { + return false + } + if !matchStringIfNonZero(row.Type, row1.Type) { + return false + } + if !matchStringOptionalIfNonZero(row.ParentName, row1.ParentName) { + return false + } + if !matchBooleanOptionalIfNonZero(row.Enabled, row1.Enabled) { + return false + } + if !matchMapStringStringIfNonZero(row.Options, row1.Options) { + return false + } + if !matchUuidOptionalIfNonZero(row.Dhcpv6Options, row1.Dhcpv6Options) { + return false + } + return true +} + +func (row *LogicalSwitchPort) OvnArgs() []string { + r := []string{} + r = append(r, OvnArgsStringMultiples("port_security", row.PortSecurity)...) + r = append(r, OvnArgsString("type", row.Type)...) + r = append(r, OvnArgsUuidOptional("dhcpv4_options", row.Dhcpv4Options)...) + r = append(r, OvnArgsMapStringString("options", row.Options)...) + r = append(r, OvnArgsUuidOptional("dhcpv6_options", row.Dhcpv6Options)...) + r = append(r, OvnArgsStringOptional("parent_name", row.ParentName)...) + r = append(r, OvnArgsBooleanOptional("enabled", row.Enabled)...) + r = append(r, OvnArgsIntegerOptional("tag", row.Tag)...) + r = append(r, OvnArgsBooleanOptional("up", row.Up)...) + r = append(r, OvnArgsMapStringString("external_ids", row.ExternalIds)...) + r = append(r, OvnArgsIntegerOptional("tag_request", row.TagRequest)...) + r = append(r, OvnArgsStringMultiples("addresses", row.Addresses)...) + r = append(r, OvnArgsStringOptional("dynamic_addresses", row.DynamicAddresses)...) + r = append(r, OvnArgsString("name", row.Name)...) + return r +} + +type LogicalSwitchPortTable []LogicalSwitchPort + +func (tbl *LogicalSwitchPortTable) NewRow() IRow { + *tbl = append(*tbl, LogicalSwitchPort{}) + return &(*tbl)[len(*tbl)-1] +} + +func (tbl LogicalSwitchPortTable) OvnTableName() string { + return "Logical_Switch_Port" +} + +func (tbl LogicalSwitchPortTable) OvnIsRoot() bool { + return false +} + +func (tbl LogicalSwitchPortTable) Rows() []IRow { + r := make([]IRow, len(tbl)) + for i := range tbl { + r[i] = &tbl[i] + } + return r +} + +func (tbl LogicalSwitchPortTable) FindOneMatchNonZeros(row1 *LogicalSwitchPort) *LogicalSwitchPort { + for i := range tbl { + row := &tbl[i] + if row.MatchNonZeros(row1) { + return row + } + } + return nil +} + +type Connection struct { + Uuid string `json:"-"` + Status map[string]string `json:"status"` + Target string `json:"target"` + MaxBackoff *int64 `json:"max_backoff"` + InactivityProbe *int64 `json:"inactivity_probe"` + OtherConfig map[string]string `json:"other_config"` + ExternalIds map[string]string `json:"external_ids"` + IsConnected bool `json:"is_connected"` +} + +func (row *Connection) OvnTableName() string { + return "Connection" +} + +func (row *Connection) OvnIsRoot() bool { + return false +} + +func (row *Connection) OvnUuid() string { + return row.Uuid +} + +func (row *Connection) OvnSetExternalIds(k, v string) { + if row.ExternalIds == nil { + row.ExternalIds = map[string]string{} + } + row.ExternalIds[k] = v +} + +func (row *Connection) OvnGetExternalIds(k string) (string, bool) { + if row.ExternalIds == nil { + return "", false + } + r, ok := row.ExternalIds[k] + return r, ok +} + +func (row *Connection) OvnRemoveExternalIds(k string) (string, bool) { + if row.ExternalIds == nil { + return "", false + } + r, ok := row.ExternalIds[k] + if ok { + delete(row.ExternalIds, k) + } + return r, ok +} + +func (row *Connection) SetColumn(name string, val interface{}) (err error) { + defer func() { + if panicErr := recover(); panicErr != nil { + err = errors.Wrapf(panicErr.(error), "%s: %#v", name, fmt.Sprintf("%#v", val)) + } + }() + switch name { + case "_uuid": + row.Uuid = ensureUuid(val) + case "target": + row.Target = ensureString(val) + case "max_backoff": + row.MaxBackoff = ensureIntegerOptional(val) + case "inactivity_probe": + row.InactivityProbe = ensureIntegerOptional(val) + case "other_config": + row.OtherConfig = ensureMapStringString(val) + case "external_ids": + row.ExternalIds = ensureMapStringString(val) + case "is_connected": + row.IsConnected = ensureBoolean(val) + case "status": + row.Status = ensureMapStringString(val) + default: + panic(ErrUnknownColumn) + } + return +} + +func (row *Connection) MatchNonZeros(row1 *Connection) bool { + if !matchMapStringStringIfNonZero(row.ExternalIds, row1.ExternalIds) { + return false + } + if !matchBooleanIfNonZero(row.IsConnected, row1.IsConnected) { + return false + } + if !matchMapStringStringIfNonZero(row.Status, row1.Status) { + return false + } + if !matchStringIfNonZero(row.Target, row1.Target) { + return false + } + if !matchIntegerOptionalIfNonZero(row.MaxBackoff, row1.MaxBackoff) { + return false + } + if !matchIntegerOptionalIfNonZero(row.InactivityProbe, row1.InactivityProbe) { + return false + } + if !matchMapStringStringIfNonZero(row.OtherConfig, row1.OtherConfig) { + return false + } + return true +} + +func (row *Connection) OvnArgs() []string { + r := []string{} + r = append(r, OvnArgsBoolean("is_connected", row.IsConnected)...) + r = append(r, OvnArgsMapStringString("status", row.Status)...) + r = append(r, OvnArgsString("target", row.Target)...) + r = append(r, OvnArgsIntegerOptional("max_backoff", row.MaxBackoff)...) + r = append(r, OvnArgsIntegerOptional("inactivity_probe", row.InactivityProbe)...) + r = append(r, OvnArgsMapStringString("other_config", row.OtherConfig)...) + r = append(r, OvnArgsMapStringString("external_ids", row.ExternalIds)...) + return r +} + +type ConnectionTable []Connection + +func (tbl *ConnectionTable) NewRow() IRow { + *tbl = append(*tbl, Connection{}) + return &(*tbl)[len(*tbl)-1] +} + +func (tbl ConnectionTable) OvnTableName() string { + return "Connection" +} + +func (tbl ConnectionTable) OvnIsRoot() bool { + return false +} + +func (tbl ConnectionTable) Rows() []IRow { + r := make([]IRow, len(tbl)) + for i := range tbl { + r[i] = &tbl[i] + } + return r +} + +func (tbl ConnectionTable) FindOneMatchNonZeros(row1 *Connection) *Connection { + for i := range tbl { + row := &tbl[i] + if row.MatchNonZeros(row1) { + return row + } + } + return nil +} + +type DNS struct { + Uuid string `json:"-"` + Records map[string]string `json:"records"` + ExternalIds map[string]string `json:"external_ids"` +} + +func (row *DNS) OvnTableName() string { + return "DNS" +} + +func (row *DNS) OvnIsRoot() bool { + return true +} + +func (row *DNS) OvnUuid() string { + return row.Uuid +} + +func (row *DNS) OvnSetExternalIds(k, v string) { + if row.ExternalIds == nil { + row.ExternalIds = map[string]string{} + } + row.ExternalIds[k] = v +} + +func (row *DNS) OvnGetExternalIds(k string) (string, bool) { + if row.ExternalIds == nil { + return "", false + } + r, ok := row.ExternalIds[k] + return r, ok +} + +func (row *DNS) OvnRemoveExternalIds(k string) (string, bool) { + if row.ExternalIds == nil { + return "", false + } + r, ok := row.ExternalIds[k] + if ok { + delete(row.ExternalIds, k) + } + return r, ok +} + +func (row *DNS) SetColumn(name string, val interface{}) (err error) { + defer func() { + if panicErr := recover(); panicErr != nil { + err = errors.Wrapf(panicErr.(error), "%s: %#v", name, fmt.Sprintf("%#v", val)) + } + }() + switch name { + case "_uuid": + row.Uuid = ensureUuid(val) + case "records": + row.Records = ensureMapStringString(val) + case "external_ids": + row.ExternalIds = ensureMapStringString(val) + default: + panic(ErrUnknownColumn) + } + return +} + +func (row *DNS) MatchNonZeros(row1 *DNS) bool { + if !matchMapStringStringIfNonZero(row.Records, row1.Records) { + return false + } + if !matchMapStringStringIfNonZero(row.ExternalIds, row1.ExternalIds) { + return false + } + return true +} + +func (row *DNS) OvnArgs() []string { + r := []string{} + r = append(r, OvnArgsMapStringString("records", row.Records)...) + r = append(r, OvnArgsMapStringString("external_ids", row.ExternalIds)...) + return r +} + +type DNSTable []DNS + +func (tbl *DNSTable) NewRow() IRow { + *tbl = append(*tbl, DNS{}) + return &(*tbl)[len(*tbl)-1] +} + +func (tbl DNSTable) OvnTableName() string { + return "DNS" +} + +func (tbl DNSTable) OvnIsRoot() bool { + return true +} + +func (tbl DNSTable) Rows() []IRow { + r := make([]IRow, len(tbl)) + for i := range tbl { + r[i] = &tbl[i] + } + return r +} + +func (tbl DNSTable) FindOneMatchNonZeros(row1 *DNS) *DNS { + for i := range tbl { + row := &tbl[i] + if row.MatchNonZeros(row1) { + return row + } + } + return nil +} + +type QoS struct { + Uuid string `json:"-"` + Match string `json:"match"` + Action map[string]int64 `json:"action"` + Bandwidth map[string]int64 `json:"bandwidth"` + ExternalIds map[string]string `json:"external_ids"` + Priority int64 `json:"priority"` + Direction string `json:"direction"` +} + +func (row *QoS) OvnTableName() string { + return "QoS" +} + +func (row *QoS) OvnIsRoot() bool { + return false +} + +func (row *QoS) OvnUuid() string { + return row.Uuid +} + +func (row *QoS) OvnSetExternalIds(k, v string) { + if row.ExternalIds == nil { + row.ExternalIds = map[string]string{} + } + row.ExternalIds[k] = v +} + +func (row *QoS) OvnGetExternalIds(k string) (string, bool) { + if row.ExternalIds == nil { + return "", false + } + r, ok := row.ExternalIds[k] + return r, ok +} + +func (row *QoS) OvnRemoveExternalIds(k string) (string, bool) { + if row.ExternalIds == nil { + return "", false + } + r, ok := row.ExternalIds[k] + if ok { + delete(row.ExternalIds, k) + } + return r, ok +} + +func (row *QoS) SetColumn(name string, val interface{}) (err error) { + defer func() { + if panicErr := recover(); panicErr != nil { + err = errors.Wrapf(panicErr.(error), "%s: %#v", name, fmt.Sprintf("%#v", val)) + } + }() + switch name { + case "_uuid": + row.Uuid = ensureUuid(val) + case "priority": + row.Priority = ensureInteger(val) + case "direction": + row.Direction = ensureString(val) + case "match": + row.Match = ensureString(val) + case "action": + row.Action = ensureMapStringInteger(val) + case "bandwidth": + row.Bandwidth = ensureMapStringInteger(val) + case "external_ids": + row.ExternalIds = ensureMapStringString(val) + default: + panic(ErrUnknownColumn) + } + return +} + +func (row *QoS) MatchNonZeros(row1 *QoS) bool { + if !matchStringIfNonZero(row.Match, row1.Match) { + return false + } + if !matchMapStringIntegerIfNonZero(row.Action, row1.Action) { + return false + } + if !matchMapStringIntegerIfNonZero(row.Bandwidth, row1.Bandwidth) { + return false + } + if !matchMapStringStringIfNonZero(row.ExternalIds, row1.ExternalIds) { + return false + } + if !matchIntegerIfNonZero(row.Priority, row1.Priority) { + return false + } + if !matchStringIfNonZero(row.Direction, row1.Direction) { + return false + } + return true +} + +func (row *QoS) OvnArgs() []string { + r := []string{} + r = append(r, OvnArgsInteger("priority", row.Priority)...) + r = append(r, OvnArgsString("direction", row.Direction)...) + r = append(r, OvnArgsString("match", row.Match)...) + r = append(r, OvnArgsMapStringInteger("action", row.Action)...) + r = append(r, OvnArgsMapStringInteger("bandwidth", row.Bandwidth)...) + r = append(r, OvnArgsMapStringString("external_ids", row.ExternalIds)...) + return r +} + +type QoSTable []QoS + +func (tbl *QoSTable) NewRow() IRow { + *tbl = append(*tbl, QoS{}) + return &(*tbl)[len(*tbl)-1] +} + +func (tbl QoSTable) OvnTableName() string { + return "QoS" +} + +func (tbl QoSTable) OvnIsRoot() bool { + return false +} + +func (tbl QoSTable) Rows() []IRow { + r := make([]IRow, len(tbl)) + for i := range tbl { + r[i] = &tbl[i] + } + return r +} + +func (tbl QoSTable) FindOneMatchNonZeros(row1 *QoS) *QoS { + for i := range tbl { + row := &tbl[i] + if row.MatchNonZeros(row1) { + return row + } + } + return nil +} + +type GatewayChassis struct { + Uuid string `json:"-"` + Name string `json:"name"` + ChassisName string `json:"chassis_name"` + Priority int64 `json:"priority"` + ExternalIds map[string]string `json:"external_ids"` + Options map[string]string `json:"options"` +} + +func (row *GatewayChassis) OvnTableName() string { + return "Gateway_Chassis" +} + +func (row *GatewayChassis) OvnIsRoot() bool { + return false +} + +func (row *GatewayChassis) OvnUuid() string { + return row.Uuid +} + +func (row *GatewayChassis) OvnSetExternalIds(k, v string) { + if row.ExternalIds == nil { + row.ExternalIds = map[string]string{} + } + row.ExternalIds[k] = v +} + +func (row *GatewayChassis) OvnGetExternalIds(k string) (string, bool) { + if row.ExternalIds == nil { + return "", false + } + r, ok := row.ExternalIds[k] + return r, ok +} + +func (row *GatewayChassis) OvnRemoveExternalIds(k string) (string, bool) { + if row.ExternalIds == nil { + return "", false + } + r, ok := row.ExternalIds[k] + if ok { + delete(row.ExternalIds, k) + } + return r, ok +} + +func (row *GatewayChassis) SetColumn(name string, val interface{}) (err error) { + defer func() { + if panicErr := recover(); panicErr != nil { + err = errors.Wrapf(panicErr.(error), "%s: %#v", name, fmt.Sprintf("%#v", val)) + } + }() + switch name { + case "_uuid": + row.Uuid = ensureUuid(val) + case "name": + row.Name = ensureString(val) + case "chassis_name": + row.ChassisName = ensureString(val) + case "priority": + row.Priority = ensureInteger(val) + case "external_ids": + row.ExternalIds = ensureMapStringString(val) + case "options": + row.Options = ensureMapStringString(val) + default: + panic(ErrUnknownColumn) + } + return +} + +func (row *GatewayChassis) MatchNonZeros(row1 *GatewayChassis) bool { + if !matchStringIfNonZero(row.ChassisName, row1.ChassisName) { + return false + } + if !matchIntegerIfNonZero(row.Priority, row1.Priority) { + return false + } + if !matchMapStringStringIfNonZero(row.ExternalIds, row1.ExternalIds) { + return false + } + if !matchMapStringStringIfNonZero(row.Options, row1.Options) { + return false + } + if !matchStringIfNonZero(row.Name, row1.Name) { + return false + } + return true +} + +func (row *GatewayChassis) OvnArgs() []string { + r := []string{} + r = append(r, OvnArgsString("name", row.Name)...) + r = append(r, OvnArgsString("chassis_name", row.ChassisName)...) + r = append(r, OvnArgsInteger("priority", row.Priority)...) + r = append(r, OvnArgsMapStringString("external_ids", row.ExternalIds)...) + r = append(r, OvnArgsMapStringString("options", row.Options)...) + return r +} + +type GatewayChassisTable []GatewayChassis + +func (tbl *GatewayChassisTable) NewRow() IRow { + *tbl = append(*tbl, GatewayChassis{}) + return &(*tbl)[len(*tbl)-1] +} + +func (tbl GatewayChassisTable) OvnTableName() string { + return "Gateway_Chassis" +} + +func (tbl GatewayChassisTable) OvnIsRoot() bool { + return false +} + +func (tbl GatewayChassisTable) Rows() []IRow { + r := make([]IRow, len(tbl)) + for i := range tbl { + r[i] = &tbl[i] + } + return r +} + +func (tbl GatewayChassisTable) FindOneMatchNonZeros(row1 *GatewayChassis) *GatewayChassis { + for i := range tbl { + row := &tbl[i] + if row.MatchNonZeros(row1) { + return row + } + } + return nil +} + +type LogicalRouterPort struct { + Uuid string `json:"-"` + Networks []string `json:"networks"` + ExternalIds map[string]string `json:"external_ids"` + Enabled *bool `json:"enabled"` + Peer *string `json:"peer"` + Ipv6RaConfigs map[string]string `json:"ipv6_ra_configs"` + GatewayChassis []string `json:"gateway_chassis"` + Mac string `json:"mac"` + Name string `json:"name"` + Options map[string]string `json:"options"` +} + +func (row *LogicalRouterPort) OvnTableName() string { + return "Logical_Router_Port" +} + +func (row *LogicalRouterPort) OvnIsRoot() bool { + return false +} + +func (row *LogicalRouterPort) OvnUuid() string { + return row.Uuid +} + +func (row *LogicalRouterPort) OvnSetExternalIds(k, v string) { + if row.ExternalIds == nil { + row.ExternalIds = map[string]string{} + } + row.ExternalIds[k] = v +} + +func (row *LogicalRouterPort) OvnGetExternalIds(k string) (string, bool) { + if row.ExternalIds == nil { + return "", false + } + r, ok := row.ExternalIds[k] + return r, ok +} + +func (row *LogicalRouterPort) OvnRemoveExternalIds(k string) (string, bool) { + if row.ExternalIds == nil { + return "", false + } + r, ok := row.ExternalIds[k] + if ok { + delete(row.ExternalIds, k) + } + return r, ok +} + +func (row *LogicalRouterPort) SetColumn(name string, val interface{}) (err error) { + defer func() { + if panicErr := recover(); panicErr != nil { + err = errors.Wrapf(panicErr.(error), "%s: %#v", name, fmt.Sprintf("%#v", val)) + } + }() + switch name { + case "_uuid": + row.Uuid = ensureUuid(val) + case "peer": + row.Peer = ensureStringOptional(val) + case "ipv6_ra_configs": + row.Ipv6RaConfigs = ensureMapStringString(val) + case "gateway_chassis": + row.GatewayChassis = ensureUuidMultiples(val) + case "mac": + row.Mac = ensureString(val) + case "name": + row.Name = ensureString(val) + case "options": + row.Options = ensureMapStringString(val) + case "networks": + row.Networks = ensureStringMultiples(val) + case "external_ids": + row.ExternalIds = ensureMapStringString(val) + case "enabled": + row.Enabled = ensureBooleanOptional(val) + default: + panic(ErrUnknownColumn) + } + return +} + +func (row *LogicalRouterPort) MatchNonZeros(row1 *LogicalRouterPort) bool { + if !matchMapStringStringIfNonZero(row.ExternalIds, row1.ExternalIds) { + return false + } + if !matchBooleanOptionalIfNonZero(row.Enabled, row1.Enabled) { + return false + } + if !matchStringMultiplesIfNonZero(row.Networks, row1.Networks) { + return false + } + if !matchMapStringStringIfNonZero(row.Ipv6RaConfigs, row1.Ipv6RaConfigs) { + return false + } + if !matchUuidMultiplesIfNonZero(row.GatewayChassis, row1.GatewayChassis) { + return false + } + if !matchStringIfNonZero(row.Mac, row1.Mac) { + return false + } + if !matchStringIfNonZero(row.Name, row1.Name) { + return false + } + if !matchMapStringStringIfNonZero(row.Options, row1.Options) { + return false + } + if !matchStringOptionalIfNonZero(row.Peer, row1.Peer) { + return false + } + return true +} + +func (row *LogicalRouterPort) OvnArgs() []string { + r := []string{} + r = append(r, OvnArgsString("mac", row.Mac)...) + r = append(r, OvnArgsString("name", row.Name)...) + r = append(r, OvnArgsMapStringString("options", row.Options)...) + r = append(r, OvnArgsStringOptional("peer", row.Peer)...) + r = append(r, OvnArgsMapStringString("ipv6_ra_configs", row.Ipv6RaConfigs)...) + r = append(r, OvnArgsUuidMultiples("gateway_chassis", row.GatewayChassis)...) + r = append(r, OvnArgsMapStringString("external_ids", row.ExternalIds)...) + r = append(r, OvnArgsBooleanOptional("enabled", row.Enabled)...) + r = append(r, OvnArgsStringMultiples("networks", row.Networks)...) + return r +} + +type LogicalRouterPortTable []LogicalRouterPort + +func (tbl *LogicalRouterPortTable) NewRow() IRow { + *tbl = append(*tbl, LogicalRouterPort{}) + return &(*tbl)[len(*tbl)-1] +} + +func (tbl LogicalRouterPortTable) OvnTableName() string { + return "Logical_Router_Port" +} + +func (tbl LogicalRouterPortTable) OvnIsRoot() bool { + return false +} + +func (tbl LogicalRouterPortTable) Rows() []IRow { + r := make([]IRow, len(tbl)) + for i := range tbl { + r[i] = &tbl[i] + } + return r +} + +func (tbl LogicalRouterPortTable) FindOneMatchNonZeros(row1 *LogicalRouterPort) *LogicalRouterPort { + for i := range tbl { + row := &tbl[i] + if row.MatchNonZeros(row1) { + return row + } + } + return nil +} + +type ACL struct { + Uuid string `json:"-"` + Direction string `json:"direction"` + Match string `json:"match"` + Action string `json:"action"` + Log bool `json:"log"` + Severity *string `json:"severity"` + ExternalIds map[string]string `json:"external_ids"` + Name *string `json:"name"` + Priority int64 `json:"priority"` +} + +func (row *ACL) OvnTableName() string { + return "ACL" +} + +func (row *ACL) OvnIsRoot() bool { + return false +} + +func (row *ACL) OvnUuid() string { + return row.Uuid +} + +func (row *ACL) OvnSetExternalIds(k, v string) { + if row.ExternalIds == nil { + row.ExternalIds = map[string]string{} + } + row.ExternalIds[k] = v +} + +func (row *ACL) OvnGetExternalIds(k string) (string, bool) { + if row.ExternalIds == nil { + return "", false + } + r, ok := row.ExternalIds[k] + return r, ok +} + +func (row *ACL) OvnRemoveExternalIds(k string) (string, bool) { + if row.ExternalIds == nil { + return "", false + } + r, ok := row.ExternalIds[k] + if ok { + delete(row.ExternalIds, k) + } + return r, ok +} + +func (row *ACL) SetColumn(name string, val interface{}) (err error) { + defer func() { + if panicErr := recover(); panicErr != nil { + err = errors.Wrapf(panicErr.(error), "%s: %#v", name, fmt.Sprintf("%#v", val)) + } + }() + switch name { + case "_uuid": + row.Uuid = ensureUuid(val) + case "match": + row.Match = ensureString(val) + case "action": + row.Action = ensureString(val) + case "log": + row.Log = ensureBoolean(val) + case "severity": + row.Severity = ensureStringOptional(val) + case "external_ids": + row.ExternalIds = ensureMapStringString(val) + case "name": + row.Name = ensureStringOptional(val) + case "priority": + row.Priority = ensureInteger(val) + case "direction": + row.Direction = ensureString(val) + default: + panic(ErrUnknownColumn) + } + return +} + +func (row *ACL) MatchNonZeros(row1 *ACL) bool { + if !matchStringIfNonZero(row.Action, row1.Action) { + return false + } + if !matchBooleanIfNonZero(row.Log, row1.Log) { + return false + } + if !matchStringOptionalIfNonZero(row.Severity, row1.Severity) { + return false + } + if !matchMapStringStringIfNonZero(row.ExternalIds, row1.ExternalIds) { + return false + } + if !matchStringOptionalIfNonZero(row.Name, row1.Name) { + return false + } + if !matchIntegerIfNonZero(row.Priority, row1.Priority) { + return false + } + if !matchStringIfNonZero(row.Direction, row1.Direction) { + return false + } + if !matchStringIfNonZero(row.Match, row1.Match) { + return false + } + return true +} + +func (row *ACL) OvnArgs() []string { + r := []string{} + r = append(r, OvnArgsStringOptional("name", row.Name)...) + r = append(r, OvnArgsInteger("priority", row.Priority)...) + r = append(r, OvnArgsString("direction", row.Direction)...) + r = append(r, OvnArgsString("match", row.Match)...) + r = append(r, OvnArgsString("action", row.Action)...) + r = append(r, OvnArgsBoolean("log", row.Log)...) + r = append(r, OvnArgsStringOptional("severity", row.Severity)...) + r = append(r, OvnArgsMapStringString("external_ids", row.ExternalIds)...) + return r +} + +type ACLTable []ACL + +func (tbl *ACLTable) NewRow() IRow { + *tbl = append(*tbl, ACL{}) + return &(*tbl)[len(*tbl)-1] +} + +func (tbl ACLTable) OvnTableName() string { + return "ACL" +} + +func (tbl ACLTable) OvnIsRoot() bool { + return false +} + +func (tbl ACLTable) Rows() []IRow { + r := make([]IRow, len(tbl)) + for i := range tbl { + r[i] = &tbl[i] + } + return r +} + +func (tbl ACLTable) FindOneMatchNonZeros(row1 *ACL) *ACL { + for i := range tbl { + row := &tbl[i] + if row.MatchNonZeros(row1) { + return row + } + } + return nil +} + +type AddressSet struct { + Uuid string `json:"-"` + Addresses []string `json:"addresses"` + ExternalIds map[string]string `json:"external_ids"` + Name string `json:"name"` +} + +func (row *AddressSet) OvnTableName() string { + return "Address_Set" +} + +func (row *AddressSet) OvnIsRoot() bool { + return true +} + +func (row *AddressSet) OvnUuid() string { + return row.Uuid +} + +func (row *AddressSet) OvnSetExternalIds(k, v string) { + if row.ExternalIds == nil { + row.ExternalIds = map[string]string{} + } + row.ExternalIds[k] = v +} + +func (row *AddressSet) OvnGetExternalIds(k string) (string, bool) { + if row.ExternalIds == nil { + return "", false + } + r, ok := row.ExternalIds[k] + return r, ok +} + +func (row *AddressSet) OvnRemoveExternalIds(k string) (string, bool) { + if row.ExternalIds == nil { + return "", false + } + r, ok := row.ExternalIds[k] + if ok { + delete(row.ExternalIds, k) + } + return r, ok +} + +func (row *AddressSet) SetColumn(name string, val interface{}) (err error) { + defer func() { + if panicErr := recover(); panicErr != nil { + err = errors.Wrapf(panicErr.(error), "%s: %#v", name, fmt.Sprintf("%#v", val)) + } + }() + switch name { + case "_uuid": + row.Uuid = ensureUuid(val) + case "external_ids": + row.ExternalIds = ensureMapStringString(val) + case "name": + row.Name = ensureString(val) + case "addresses": + row.Addresses = ensureStringMultiples(val) + default: + panic(ErrUnknownColumn) + } + return +} + +func (row *AddressSet) MatchNonZeros(row1 *AddressSet) bool { + if !matchMapStringStringIfNonZero(row.ExternalIds, row1.ExternalIds) { + return false + } + if !matchStringIfNonZero(row.Name, row1.Name) { + return false + } + if !matchStringMultiplesIfNonZero(row.Addresses, row1.Addresses) { + return false + } + return true +} + +func (row *AddressSet) OvnArgs() []string { + r := []string{} + r = append(r, OvnArgsString("name", row.Name)...) + r = append(r, OvnArgsStringMultiples("addresses", row.Addresses)...) + r = append(r, OvnArgsMapStringString("external_ids", row.ExternalIds)...) + return r +} + +type AddressSetTable []AddressSet + +func (tbl *AddressSetTable) NewRow() IRow { + *tbl = append(*tbl, AddressSet{}) + return &(*tbl)[len(*tbl)-1] +} + +func (tbl AddressSetTable) OvnTableName() string { + return "Address_Set" +} + +func (tbl AddressSetTable) OvnIsRoot() bool { + return true +} + +func (tbl AddressSetTable) Rows() []IRow { + r := make([]IRow, len(tbl)) + for i := range tbl { + r[i] = &tbl[i] + } + return r +} + +func (tbl AddressSetTable) FindOneMatchNonZeros(row1 *AddressSet) *AddressSet { + for i := range tbl { + row := &tbl[i] + if row.MatchNonZeros(row1) { + return row + } + } + return nil +} + +type LogicalSwitch struct { + Uuid string `json:"-"` + ExternalIds map[string]string `json:"external_ids"` + Name string `json:"name"` + Ports []string `json:"ports"` + Acls []string `json:"acls"` + QosRules []string `json:"qos_rules"` + LoadBalancer []string `json:"load_balancer"` + DnsRecords []string `json:"dns_records"` + OtherConfig map[string]string `json:"other_config"` +} + +func (row *LogicalSwitch) OvnTableName() string { + return "Logical_Switch" +} + +func (row *LogicalSwitch) OvnIsRoot() bool { + return true +} + +func (row *LogicalSwitch) OvnUuid() string { + return row.Uuid +} + +func (row *LogicalSwitch) OvnSetExternalIds(k, v string) { + if row.ExternalIds == nil { + row.ExternalIds = map[string]string{} + } + row.ExternalIds[k] = v +} + +func (row *LogicalSwitch) OvnGetExternalIds(k string) (string, bool) { + if row.ExternalIds == nil { + return "", false + } + r, ok := row.ExternalIds[k] + return r, ok +} + +func (row *LogicalSwitch) OvnRemoveExternalIds(k string) (string, bool) { + if row.ExternalIds == nil { + return "", false + } + r, ok := row.ExternalIds[k] + if ok { + delete(row.ExternalIds, k) + } + return r, ok +} + +func (row *LogicalSwitch) SetColumn(name string, val interface{}) (err error) { + defer func() { + if panicErr := recover(); panicErr != nil { + err = errors.Wrapf(panicErr.(error), "%s: %#v", name, fmt.Sprintf("%#v", val)) + } + }() + switch name { + case "_uuid": + row.Uuid = ensureUuid(val) + case "qos_rules": + row.QosRules = ensureUuidMultiples(val) + case "load_balancer": + row.LoadBalancer = ensureUuidMultiples(val) + case "dns_records": + row.DnsRecords = ensureUuidMultiples(val) + case "other_config": + row.OtherConfig = ensureMapStringString(val) + case "external_ids": + row.ExternalIds = ensureMapStringString(val) + case "name": + row.Name = ensureString(val) + case "ports": + row.Ports = ensureUuidMultiples(val) + case "acls": + row.Acls = ensureUuidMultiples(val) + default: + panic(ErrUnknownColumn) + } + return +} + +func (row *LogicalSwitch) MatchNonZeros(row1 *LogicalSwitch) bool { + if !matchUuidMultiplesIfNonZero(row.QosRules, row1.QosRules) { + return false + } + if !matchUuidMultiplesIfNonZero(row.LoadBalancer, row1.LoadBalancer) { + return false + } + if !matchUuidMultiplesIfNonZero(row.DnsRecords, row1.DnsRecords) { + return false + } + if !matchMapStringStringIfNonZero(row.OtherConfig, row1.OtherConfig) { + return false + } + if !matchMapStringStringIfNonZero(row.ExternalIds, row1.ExternalIds) { + return false + } + if !matchStringIfNonZero(row.Name, row1.Name) { + return false + } + if !matchUuidMultiplesIfNonZero(row.Ports, row1.Ports) { + return false + } + if !matchUuidMultiplesIfNonZero(row.Acls, row1.Acls) { + return false + } + return true +} + +func (row *LogicalSwitch) OvnArgs() []string { + r := []string{} + r = append(r, OvnArgsUuidMultiples("qos_rules", row.QosRules)...) + r = append(r, OvnArgsUuidMultiples("load_balancer", row.LoadBalancer)...) + r = append(r, OvnArgsUuidMultiples("dns_records", row.DnsRecords)...) + r = append(r, OvnArgsMapStringString("other_config", row.OtherConfig)...) + r = append(r, OvnArgsMapStringString("external_ids", row.ExternalIds)...) + r = append(r, OvnArgsString("name", row.Name)...) + r = append(r, OvnArgsUuidMultiples("ports", row.Ports)...) + r = append(r, OvnArgsUuidMultiples("acls", row.Acls)...) + return r +} + +type LogicalSwitchTable []LogicalSwitch + +func (tbl *LogicalSwitchTable) NewRow() IRow { + *tbl = append(*tbl, LogicalSwitch{}) + return &(*tbl)[len(*tbl)-1] +} + +func (tbl LogicalSwitchTable) OvnTableName() string { + return "Logical_Switch" +} + +func (tbl LogicalSwitchTable) OvnIsRoot() bool { + return true +} + +func (tbl LogicalSwitchTable) Rows() []IRow { + r := make([]IRow, len(tbl)) + for i := range tbl { + r[i] = &tbl[i] + } + return r +} + +func (tbl LogicalSwitchTable) FindOneMatchNonZeros(row1 *LogicalSwitch) *LogicalSwitch { + for i := range tbl { + row := &tbl[i] + if row.MatchNonZeros(row1) { + return row + } + } + return nil +} + +type NAT struct { + Uuid string `json:"-"` + Type string `json:"type"` + ExternalIds map[string]string `json:"external_ids"` + ExternalIp string `json:"external_ip"` + ExternalMac *string `json:"external_mac"` + LogicalIp string `json:"logical_ip"` + LogicalPort *string `json:"logical_port"` +} + +func (row *NAT) OvnTableName() string { + return "NAT" +} + +func (row *NAT) OvnIsRoot() bool { + return false +} + +func (row *NAT) OvnUuid() string { + return row.Uuid +} + +func (row *NAT) OvnSetExternalIds(k, v string) { + if row.ExternalIds == nil { + row.ExternalIds = map[string]string{} + } + row.ExternalIds[k] = v +} + +func (row *NAT) OvnGetExternalIds(k string) (string, bool) { + if row.ExternalIds == nil { + return "", false + } + r, ok := row.ExternalIds[k] + return r, ok +} + +func (row *NAT) OvnRemoveExternalIds(k string) (string, bool) { + if row.ExternalIds == nil { + return "", false + } + r, ok := row.ExternalIds[k] + if ok { + delete(row.ExternalIds, k) + } + return r, ok +} + +func (row *NAT) SetColumn(name string, val interface{}) (err error) { + defer func() { + if panicErr := recover(); panicErr != nil { + err = errors.Wrapf(panicErr.(error), "%s: %#v", name, fmt.Sprintf("%#v", val)) + } + }() + switch name { + case "_uuid": + row.Uuid = ensureUuid(val) + case "type": + row.Type = ensureString(val) + case "external_ids": + row.ExternalIds = ensureMapStringString(val) + case "external_ip": + row.ExternalIp = ensureString(val) + case "external_mac": + row.ExternalMac = ensureStringOptional(val) + case "logical_ip": + row.LogicalIp = ensureString(val) + case "logical_port": + row.LogicalPort = ensureStringOptional(val) + default: + panic(ErrUnknownColumn) + } + return +} + +func (row *NAT) MatchNonZeros(row1 *NAT) bool { + if !matchMapStringStringIfNonZero(row.ExternalIds, row1.ExternalIds) { + return false + } + if !matchStringIfNonZero(row.ExternalIp, row1.ExternalIp) { + return false + } + if !matchStringOptionalIfNonZero(row.ExternalMac, row1.ExternalMac) { + return false + } + if !matchStringIfNonZero(row.LogicalIp, row1.LogicalIp) { + return false + } + if !matchStringOptionalIfNonZero(row.LogicalPort, row1.LogicalPort) { + return false + } + if !matchStringIfNonZero(row.Type, row1.Type) { + return false + } + return true +} + +func (row *NAT) OvnArgs() []string { + r := []string{} + r = append(r, OvnArgsMapStringString("external_ids", row.ExternalIds)...) + r = append(r, OvnArgsString("external_ip", row.ExternalIp)...) + r = append(r, OvnArgsStringOptional("external_mac", row.ExternalMac)...) + r = append(r, OvnArgsString("logical_ip", row.LogicalIp)...) + r = append(r, OvnArgsStringOptional("logical_port", row.LogicalPort)...) + r = append(r, OvnArgsString("type", row.Type)...) + return r +} + +type NATTable []NAT + +func (tbl *NATTable) NewRow() IRow { + *tbl = append(*tbl, NAT{}) + return &(*tbl)[len(*tbl)-1] +} + +func (tbl NATTable) OvnTableName() string { + return "NAT" +} + +func (tbl NATTable) OvnIsRoot() bool { + return false +} + +func (tbl NATTable) Rows() []IRow { + r := make([]IRow, len(tbl)) + for i := range tbl { + r[i] = &tbl[i] + } + return r +} + +func (tbl NATTable) FindOneMatchNonZeros(row1 *NAT) *NAT { + for i := range tbl { + row := &tbl[i] + if row.MatchNonZeros(row1) { + return row + } + } + return nil +} + +type DHCPOptions struct { + Uuid string `json:"-"` + ExternalIds map[string]string `json:"external_ids"` + Cidr string `json:"cidr"` + Options map[string]string `json:"options"` +} + +func (row *DHCPOptions) OvnTableName() string { + return "DHCP_Options" +} + +func (row *DHCPOptions) OvnIsRoot() bool { + return true +} + +func (row *DHCPOptions) OvnUuid() string { + return row.Uuid +} + +func (row *DHCPOptions) OvnSetExternalIds(k, v string) { + if row.ExternalIds == nil { + row.ExternalIds = map[string]string{} + } + row.ExternalIds[k] = v +} + +func (row *DHCPOptions) OvnGetExternalIds(k string) (string, bool) { + if row.ExternalIds == nil { + return "", false + } + r, ok := row.ExternalIds[k] + return r, ok +} + +func (row *DHCPOptions) OvnRemoveExternalIds(k string) (string, bool) { + if row.ExternalIds == nil { + return "", false + } + r, ok := row.ExternalIds[k] + if ok { + delete(row.ExternalIds, k) + } + return r, ok +} + +func (row *DHCPOptions) SetColumn(name string, val interface{}) (err error) { + defer func() { + if panicErr := recover(); panicErr != nil { + err = errors.Wrapf(panicErr.(error), "%s: %#v", name, fmt.Sprintf("%#v", val)) + } + }() + switch name { + case "_uuid": + row.Uuid = ensureUuid(val) + case "cidr": + row.Cidr = ensureString(val) + case "options": + row.Options = ensureMapStringString(val) + case "external_ids": + row.ExternalIds = ensureMapStringString(val) + default: + panic(ErrUnknownColumn) + } + return +} + +func (row *DHCPOptions) MatchNonZeros(row1 *DHCPOptions) bool { + if !matchStringIfNonZero(row.Cidr, row1.Cidr) { + return false + } + if !matchMapStringStringIfNonZero(row.Options, row1.Options) { + return false + } + if !matchMapStringStringIfNonZero(row.ExternalIds, row1.ExternalIds) { + return false + } + return true +} + +func (row *DHCPOptions) OvnArgs() []string { + r := []string{} + r = append(r, OvnArgsString("cidr", row.Cidr)...) + r = append(r, OvnArgsMapStringString("options", row.Options)...) + r = append(r, OvnArgsMapStringString("external_ids", row.ExternalIds)...) + return r +} + +type DHCPOptionsTable []DHCPOptions + +func (tbl *DHCPOptionsTable) NewRow() IRow { + *tbl = append(*tbl, DHCPOptions{}) + return &(*tbl)[len(*tbl)-1] +} + +func (tbl DHCPOptionsTable) OvnTableName() string { + return "DHCP_Options" +} + +func (tbl DHCPOptionsTable) OvnIsRoot() bool { + return true +} + +func (tbl DHCPOptionsTable) Rows() []IRow { + r := make([]IRow, len(tbl)) + for i := range tbl { + r[i] = &tbl[i] + } + return r +} + +func (tbl DHCPOptionsTable) FindOneMatchNonZeros(row1 *DHCPOptions) *DHCPOptions { + for i := range tbl { + row := &tbl[i] + if row.MatchNonZeros(row1) { + return row + } + } + return nil +} + +type LoadBalancer struct { + Uuid string `json:"-"` + ExternalIds map[string]string `json:"external_ids"` + Name string `json:"name"` + Vips map[string]string `json:"vips"` + Protocol *string `json:"protocol"` +} + +func (row *LoadBalancer) OvnTableName() string { + return "Load_Balancer" +} + +func (row *LoadBalancer) OvnIsRoot() bool { + return true +} + +func (row *LoadBalancer) OvnUuid() string { + return row.Uuid +} + +func (row *LoadBalancer) OvnSetExternalIds(k, v string) { + if row.ExternalIds == nil { + row.ExternalIds = map[string]string{} + } + row.ExternalIds[k] = v +} + +func (row *LoadBalancer) OvnGetExternalIds(k string) (string, bool) { + if row.ExternalIds == nil { + return "", false + } + r, ok := row.ExternalIds[k] + return r, ok +} + +func (row *LoadBalancer) OvnRemoveExternalIds(k string) (string, bool) { + if row.ExternalIds == nil { + return "", false + } + r, ok := row.ExternalIds[k] + if ok { + delete(row.ExternalIds, k) + } + return r, ok +} + +func (row *LoadBalancer) SetColumn(name string, val interface{}) (err error) { + defer func() { + if panicErr := recover(); panicErr != nil { + err = errors.Wrapf(panicErr.(error), "%s: %#v", name, fmt.Sprintf("%#v", val)) + } + }() + switch name { + case "_uuid": + row.Uuid = ensureUuid(val) + case "name": + row.Name = ensureString(val) + case "vips": + row.Vips = ensureMapStringString(val) + case "protocol": + row.Protocol = ensureStringOptional(val) + case "external_ids": + row.ExternalIds = ensureMapStringString(val) + default: + panic(ErrUnknownColumn) + } + return +} + +func (row *LoadBalancer) MatchNonZeros(row1 *LoadBalancer) bool { + if !matchMapStringStringIfNonZero(row.Vips, row1.Vips) { + return false + } + if !matchStringOptionalIfNonZero(row.Protocol, row1.Protocol) { + return false + } + if !matchMapStringStringIfNonZero(row.ExternalIds, row1.ExternalIds) { + return false + } + if !matchStringIfNonZero(row.Name, row1.Name) { + return false + } + return true +} + +func (row *LoadBalancer) OvnArgs() []string { + r := []string{} + r = append(r, OvnArgsString("name", row.Name)...) + r = append(r, OvnArgsMapStringString("vips", row.Vips)...) + r = append(r, OvnArgsStringOptional("protocol", row.Protocol)...) + r = append(r, OvnArgsMapStringString("external_ids", row.ExternalIds)...) + return r +} + +type LoadBalancerTable []LoadBalancer + +func (tbl *LoadBalancerTable) NewRow() IRow { + *tbl = append(*tbl, LoadBalancer{}) + return &(*tbl)[len(*tbl)-1] +} + +func (tbl LoadBalancerTable) OvnTableName() string { + return "Load_Balancer" +} + +func (tbl LoadBalancerTable) OvnIsRoot() bool { + return true +} + +func (tbl LoadBalancerTable) Rows() []IRow { + r := make([]IRow, len(tbl)) + for i := range tbl { + r[i] = &tbl[i] + } + return r +} + +func (tbl LoadBalancerTable) FindOneMatchNonZeros(row1 *LoadBalancer) *LoadBalancer { + for i := range tbl { + row := &tbl[i] + if row.MatchNonZeros(row1) { + return row + } + } + return nil +} + +type SSL struct { + Uuid string `json:"-"` + SslCiphers string `json:"ssl_ciphers"` + ExternalIds map[string]string `json:"external_ids"` + PrivateKey string `json:"private_key"` + Certificate string `json:"certificate"` + CaCert string `json:"ca_cert"` + BootstrapCaCert bool `json:"bootstrap_ca_cert"` + SslProtocols string `json:"ssl_protocols"` +} + +func (row *SSL) OvnTableName() string { + return "SSL" +} + +func (row *SSL) OvnIsRoot() bool { + return false +} + +func (row *SSL) OvnUuid() string { + return row.Uuid +} + +func (row *SSL) OvnSetExternalIds(k, v string) { + if row.ExternalIds == nil { + row.ExternalIds = map[string]string{} + } + row.ExternalIds[k] = v +} + +func (row *SSL) OvnGetExternalIds(k string) (string, bool) { + if row.ExternalIds == nil { + return "", false + } + r, ok := row.ExternalIds[k] + return r, ok +} + +func (row *SSL) OvnRemoveExternalIds(k string) (string, bool) { + if row.ExternalIds == nil { + return "", false + } + r, ok := row.ExternalIds[k] + if ok { + delete(row.ExternalIds, k) + } + return r, ok +} + +func (row *SSL) SetColumn(name string, val interface{}) (err error) { + defer func() { + if panicErr := recover(); panicErr != nil { + err = errors.Wrapf(panicErr.(error), "%s: %#v", name, fmt.Sprintf("%#v", val)) + } + }() + switch name { + case "_uuid": + row.Uuid = ensureUuid(val) + case "ssl_protocols": + row.SslProtocols = ensureString(val) + case "ssl_ciphers": + row.SslCiphers = ensureString(val) + case "external_ids": + row.ExternalIds = ensureMapStringString(val) + case "private_key": + row.PrivateKey = ensureString(val) + case "certificate": + row.Certificate = ensureString(val) + case "ca_cert": + row.CaCert = ensureString(val) + case "bootstrap_ca_cert": + row.BootstrapCaCert = ensureBoolean(val) + default: + panic(ErrUnknownColumn) + } + return +} + +func (row *SSL) MatchNonZeros(row1 *SSL) bool { + if !matchMapStringStringIfNonZero(row.ExternalIds, row1.ExternalIds) { + return false + } + if !matchStringIfNonZero(row.PrivateKey, row1.PrivateKey) { + return false + } + if !matchStringIfNonZero(row.Certificate, row1.Certificate) { + return false + } + if !matchStringIfNonZero(row.CaCert, row1.CaCert) { + return false + } + if !matchBooleanIfNonZero(row.BootstrapCaCert, row1.BootstrapCaCert) { + return false + } + if !matchStringIfNonZero(row.SslProtocols, row1.SslProtocols) { + return false + } + if !matchStringIfNonZero(row.SslCiphers, row1.SslCiphers) { + return false + } + return true +} + +func (row *SSL) OvnArgs() []string { + r := []string{} + r = append(r, OvnArgsBoolean("bootstrap_ca_cert", row.BootstrapCaCert)...) + r = append(r, OvnArgsString("ssl_protocols", row.SslProtocols)...) + r = append(r, OvnArgsString("ssl_ciphers", row.SslCiphers)...) + r = append(r, OvnArgsMapStringString("external_ids", row.ExternalIds)...) + r = append(r, OvnArgsString("private_key", row.PrivateKey)...) + r = append(r, OvnArgsString("certificate", row.Certificate)...) + r = append(r, OvnArgsString("ca_cert", row.CaCert)...) + return r +} + +type SSLTable []SSL + +func (tbl *SSLTable) NewRow() IRow { + *tbl = append(*tbl, SSL{}) + return &(*tbl)[len(*tbl)-1] +} + +func (tbl SSLTable) OvnTableName() string { + return "SSL" +} + +func (tbl SSLTable) OvnIsRoot() bool { + return false +} + +func (tbl SSLTable) Rows() []IRow { + r := make([]IRow, len(tbl)) + for i := range tbl { + r[i] = &tbl[i] + } + return r +} + +func (tbl SSLTable) FindOneMatchNonZeros(row1 *SSL) *SSL { + for i := range tbl { + row := &tbl[i] + if row.MatchNonZeros(row1) { + return row + } + } + return nil +} + +type NBGlobal struct { + Uuid string `json:"-"` + Connections []string `json:"connections"` + Ssl *string `json:"ssl"` + NbCfg int64 `json:"nb_cfg"` + SbCfg int64 `json:"sb_cfg"` + HvCfg int64 `json:"hv_cfg"` + ExternalIds map[string]string `json:"external_ids"` +} + +func (row *NBGlobal) OvnTableName() string { + return "NB_Global" +} + +func (row *NBGlobal) OvnIsRoot() bool { + return true +} + +func (row *NBGlobal) OvnUuid() string { + return row.Uuid +} + +func (row *NBGlobal) OvnSetExternalIds(k, v string) { + if row.ExternalIds == nil { + row.ExternalIds = map[string]string{} + } + row.ExternalIds[k] = v +} + +func (row *NBGlobal) OvnGetExternalIds(k string) (string, bool) { + if row.ExternalIds == nil { + return "", false + } + r, ok := row.ExternalIds[k] + return r, ok +} + +func (row *NBGlobal) OvnRemoveExternalIds(k string) (string, bool) { + if row.ExternalIds == nil { + return "", false + } + r, ok := row.ExternalIds[k] + if ok { + delete(row.ExternalIds, k) + } + return r, ok +} + +func (row *NBGlobal) SetColumn(name string, val interface{}) (err error) { + defer func() { + if panicErr := recover(); panicErr != nil { + err = errors.Wrapf(panicErr.(error), "%s: %#v", name, fmt.Sprintf("%#v", val)) + } + }() + switch name { + case "_uuid": + row.Uuid = ensureUuid(val) + case "sb_cfg": + row.SbCfg = ensureInteger(val) + case "hv_cfg": + row.HvCfg = ensureInteger(val) + case "external_ids": + row.ExternalIds = ensureMapStringString(val) + case "connections": + row.Connections = ensureUuidMultiples(val) + case "ssl": + row.Ssl = ensureUuidOptional(val) + case "nb_cfg": + row.NbCfg = ensureInteger(val) + default: + panic(ErrUnknownColumn) + } + return +} + +func (row *NBGlobal) MatchNonZeros(row1 *NBGlobal) bool { + if !matchUuidOptionalIfNonZero(row.Ssl, row1.Ssl) { + return false + } + if !matchIntegerIfNonZero(row.NbCfg, row1.NbCfg) { + return false + } + if !matchIntegerIfNonZero(row.SbCfg, row1.SbCfg) { + return false + } + if !matchIntegerIfNonZero(row.HvCfg, row1.HvCfg) { + return false + } + if !matchMapStringStringIfNonZero(row.ExternalIds, row1.ExternalIds) { + return false + } + if !matchUuidMultiplesIfNonZero(row.Connections, row1.Connections) { + return false + } + return true +} + +func (row *NBGlobal) OvnArgs() []string { + r := []string{} + r = append(r, OvnArgsInteger("sb_cfg", row.SbCfg)...) + r = append(r, OvnArgsInteger("hv_cfg", row.HvCfg)...) + r = append(r, OvnArgsMapStringString("external_ids", row.ExternalIds)...) + r = append(r, OvnArgsUuidMultiples("connections", row.Connections)...) + r = append(r, OvnArgsUuidOptional("ssl", row.Ssl)...) + r = append(r, OvnArgsInteger("nb_cfg", row.NbCfg)...) + return r +} + +type NBGlobalTable []NBGlobal + +func (tbl *NBGlobalTable) NewRow() IRow { + *tbl = append(*tbl, NBGlobal{}) + return &(*tbl)[len(*tbl)-1] +} + +func (tbl NBGlobalTable) OvnTableName() string { + return "NB_Global" +} + +func (tbl NBGlobalTable) OvnIsRoot() bool { + return true +} + +func (tbl NBGlobalTable) Rows() []IRow { + r := make([]IRow, len(tbl)) + for i := range tbl { + r[i] = &tbl[i] + } + return r +} + +func (tbl NBGlobalTable) FindOneMatchNonZeros(row1 *NBGlobal) *NBGlobal { + for i := range tbl { + row := &tbl[i] + if row.MatchNonZeros(row1) { + return row + } + } + return nil +} + +type LogicalRouterStaticRoute struct { + Uuid string `json:"-"` + IpPrefix string `json:"ip_prefix"` + Policy *string `json:"policy"` + Nexthop string `json:"nexthop"` + OutputPort *string `json:"output_port"` + ExternalIds map[string]string `json:"external_ids"` +} + +func (row *LogicalRouterStaticRoute) OvnTableName() string { + return "Logical_Router_Static_Route" +} + +func (row *LogicalRouterStaticRoute) OvnIsRoot() bool { + return false +} + +func (row *LogicalRouterStaticRoute) OvnUuid() string { + return row.Uuid +} + +func (row *LogicalRouterStaticRoute) OvnSetExternalIds(k, v string) { + if row.ExternalIds == nil { + row.ExternalIds = map[string]string{} + } + row.ExternalIds[k] = v +} + +func (row *LogicalRouterStaticRoute) OvnGetExternalIds(k string) (string, bool) { + if row.ExternalIds == nil { + return "", false + } + r, ok := row.ExternalIds[k] + return r, ok +} + +func (row *LogicalRouterStaticRoute) OvnRemoveExternalIds(k string) (string, bool) { + if row.ExternalIds == nil { + return "", false + } + r, ok := row.ExternalIds[k] + if ok { + delete(row.ExternalIds, k) + } + return r, ok +} + +func (row *LogicalRouterStaticRoute) SetColumn(name string, val interface{}) (err error) { + defer func() { + if panicErr := recover(); panicErr != nil { + err = errors.Wrapf(panicErr.(error), "%s: %#v", name, fmt.Sprintf("%#v", val)) + } + }() + switch name { + case "_uuid": + row.Uuid = ensureUuid(val) + case "external_ids": + row.ExternalIds = ensureMapStringString(val) + case "ip_prefix": + row.IpPrefix = ensureString(val) + case "policy": + row.Policy = ensureStringOptional(val) + case "nexthop": + row.Nexthop = ensureString(val) + case "output_port": + row.OutputPort = ensureStringOptional(val) + default: + panic(ErrUnknownColumn) + } + return +} + +func (row *LogicalRouterStaticRoute) MatchNonZeros(row1 *LogicalRouterStaticRoute) bool { + if !matchStringIfNonZero(row.IpPrefix, row1.IpPrefix) { + return false + } + if !matchStringOptionalIfNonZero(row.Policy, row1.Policy) { + return false + } + if !matchStringIfNonZero(row.Nexthop, row1.Nexthop) { + return false + } + if !matchStringOptionalIfNonZero(row.OutputPort, row1.OutputPort) { + return false + } + if !matchMapStringStringIfNonZero(row.ExternalIds, row1.ExternalIds) { + return false + } + return true +} + +func (row *LogicalRouterStaticRoute) OvnArgs() []string { + r := []string{} + r = append(r, OvnArgsString("ip_prefix", row.IpPrefix)...) + r = append(r, OvnArgsStringOptional("policy", row.Policy)...) + r = append(r, OvnArgsString("nexthop", row.Nexthop)...) + r = append(r, OvnArgsStringOptional("output_port", row.OutputPort)...) + r = append(r, OvnArgsMapStringString("external_ids", row.ExternalIds)...) + return r +} + +type LogicalRouterStaticRouteTable []LogicalRouterStaticRoute + +func (tbl *LogicalRouterStaticRouteTable) NewRow() IRow { + *tbl = append(*tbl, LogicalRouterStaticRoute{}) + return &(*tbl)[len(*tbl)-1] +} + +func (tbl LogicalRouterStaticRouteTable) OvnTableName() string { + return "Logical_Router_Static_Route" +} + +func (tbl LogicalRouterStaticRouteTable) OvnIsRoot() bool { + return false +} + +func (tbl LogicalRouterStaticRouteTable) Rows() []IRow { + r := make([]IRow, len(tbl)) + for i := range tbl { + r[i] = &tbl[i] + } + return r +} + +func (tbl LogicalRouterStaticRouteTable) FindOneMatchNonZeros(row1 *LogicalRouterStaticRoute) *LogicalRouterStaticRoute { + for i := range tbl { + row := &tbl[i] + if row.MatchNonZeros(row1) { + return row + } + } + return nil +} diff --git a/pkg/vpcagent/ovnutil/schema_test.go b/pkg/vpcagent/ovnutil/schema_test.go new file mode 100644 index 0000000000..4c5118e7fe --- /dev/null +++ b/pkg/vpcagent/ovnutil/schema_test.go @@ -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) + } + }) +} diff --git a/pkg/vpcagent/ovnutil/unmarshal.go b/pkg/vpcagent/ovnutil/unmarshal.go new file mode 100644 index 0000000000..426f9d878d --- /dev/null +++ b/pkg/vpcagent/ovnutil/unmarshal.go @@ -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 +} diff --git a/pkg/vpcagent/worker/doc.go b/pkg/vpcagent/worker/doc.go new file mode 100644 index 0000000000..4309d36972 --- /dev/null +++ b/pkg/vpcagent/worker/doc.go @@ -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" diff --git a/pkg/vpcagent/worker/worker.go b/pkg/vpcagent/worker/worker.go new file mode 100644 index 0000000000..e4b760f59b --- /dev/null +++ b/pkg/vpcagent/worker/worker.go @@ -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 +}