mirror of
https://github.com/yunionio/cloudpods.git
synced 2026-05-19 07:37:55 +08:00
279 lines
7.9 KiB
Go
279 lines
7.9 KiB
Go
// Copyright 2019 Yunion
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
package models
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
|
|
"yunion.io/x/jsonutils"
|
|
"yunion.io/x/pkg/errors"
|
|
"yunion.io/x/pkg/util/rand"
|
|
"yunion.io/x/sqlchemy"
|
|
|
|
"yunion.io/x/onecloud/pkg/apis"
|
|
api "yunion.io/x/onecloud/pkg/apis/cloudnet"
|
|
"yunion.io/x/onecloud/pkg/cloudcommon/db"
|
|
"yunion.io/x/onecloud/pkg/cloudcommon/validators"
|
|
"yunion.io/x/onecloud/pkg/httperrors"
|
|
"yunion.io/x/onecloud/pkg/mcclient"
|
|
)
|
|
|
|
type SRoute struct {
|
|
db.SStandaloneResourceBase
|
|
|
|
IfaceId string `length:"32" nullable:"false" list:"user" create:"required"`
|
|
Ifname string `length:"32" nullable:"false" list:"user" create:"optional"`
|
|
|
|
Network string `length:"32" nullable:"false" list:"user" update:"user" create:"required"`
|
|
Gateway string `length:"32" nullable:"false" list:"user" update:"user" create:"optional"`
|
|
|
|
RouterId string `length:"32" nullable:"false" list:"user" create:"optional"`
|
|
}
|
|
|
|
type SRouteManager struct {
|
|
db.SStandaloneResourceBaseManager
|
|
}
|
|
|
|
var RouteManager *SRouteManager
|
|
|
|
func init() {
|
|
RouteManager = &SRouteManager{
|
|
SStandaloneResourceBaseManager: db.NewStandaloneResourceBaseManager(
|
|
SRoute{},
|
|
"routes_tbl",
|
|
"route",
|
|
"routes",
|
|
),
|
|
}
|
|
RouteManager.SetVirtualObject(RouteManager)
|
|
}
|
|
|
|
func (man *SRouteManager) ValidateCreateData(ctx context.Context, userCred mcclient.TokenCredential, ownerId mcclient.IIdentityProvider, query jsonutils.JSONObject, data *jsonutils.JSONDict) (*jsonutils.JSONDict, error) {
|
|
input := apis.StandaloneResourceCreateInput{}
|
|
err := data.Unmarshal(&input)
|
|
if err != nil {
|
|
return nil, httperrors.NewInternalServerError("unmarshal StandaloneResourceCreateInput fail %s", err)
|
|
}
|
|
input, err = man.SStandaloneResourceBaseManager.ValidateCreateData(ctx, userCred, ownerId, query, input)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
data.Update(jsonutils.Marshal(input))
|
|
|
|
ifaceV := validators.NewModelIdOrNameValidator("iface", "iface", ownerId)
|
|
networkV := validators.NewIPv4PrefixValidator("network")
|
|
gatewayV := validators.NewIPv4AddrValidator("gateway")
|
|
vs := []validators.IValidator{
|
|
networkV,
|
|
gatewayV.Optional(true),
|
|
ifaceV,
|
|
}
|
|
for _, v := range vs {
|
|
if err := v.Validate(data); err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
iface := ifaceV.Model.(*SIface)
|
|
network := networkV.Value.String()
|
|
routerId := iface.RouterId
|
|
{
|
|
if routes, err := man.getByFilter(map[string]string{
|
|
"router_id": routerId,
|
|
"network": network,
|
|
}); err != nil {
|
|
return nil, httperrors.NewConflictError("query existing route to network %s: %v", network, err)
|
|
} else if len(routes) > 0 {
|
|
return nil, httperrors.NewConflictError("route to %s already exist: %s(%s)", network, routes[0].Name, routes[0].Id)
|
|
}
|
|
}
|
|
|
|
data.Set("router_id", jsonutils.NewString(routerId))
|
|
data.Set("ifname", jsonutils.NewString(iface.Ifname))
|
|
routerV := validators.NewModelIdOrNameValidator("router", "router", ownerId)
|
|
if err := routerV.Validate(data); err != nil {
|
|
return nil, err
|
|
}
|
|
if !data.Contains("name") {
|
|
router := routerV.Model.(*SRouter)
|
|
data.Set("name", jsonutils.NewString(
|
|
router.Name+"-"+iface.Name+"-"+rand.String(4)),
|
|
)
|
|
}
|
|
return data, nil
|
|
}
|
|
|
|
// 虚拟路由器路由列表
|
|
func (man *SRouteManager) ListItemFilter(ctx context.Context, q *sqlchemy.SQuery, userCred mcclient.TokenCredential, query jsonutils.JSONObject) (*sqlchemy.SQuery, error) {
|
|
input := apis.StandaloneResourceListInput{}
|
|
err := query.Unmarshal(&input)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
q, err = man.SStandaloneResourceBaseManager.ListItemFilter(ctx, q, userCred, input)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
data := query.(*jsonutils.JSONDict)
|
|
q, err = validators.ApplyModelFilters(q, data, []*validators.ModelFilterOptions{
|
|
{Key: "router", ModelKeyword: "router", OwnerId: userCred},
|
|
{Key: "iface", ModelKeyword: "iface", OwnerId: userCred},
|
|
})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return q, nil
|
|
}
|
|
|
|
func (route *SRoute) ValidateUpdateData(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input api.RouteUpdateInput) (api.RouteUpdateInput, error) {
|
|
var err error
|
|
input.StandaloneResourceBaseUpdateInput, err = route.SStandaloneResourceBase.ValidateUpdateData(ctx, userCred, query, input.StandaloneResourceBaseUpdateInput)
|
|
if err != nil {
|
|
return input, errors.Wrap(err, "SStandaloneResourceBase.ValidateUpdateData")
|
|
}
|
|
data := jsonutils.Marshal(input).(*jsonutils.JSONDict)
|
|
vs := []validators.IValidator{
|
|
validators.NewIPv4PrefixValidator("network"),
|
|
validators.NewIPv4AddrValidator("gateway"),
|
|
}
|
|
for _, v := range vs {
|
|
v.Optional(true)
|
|
if err := v.Validate(data); err != nil {
|
|
return input, err
|
|
}
|
|
}
|
|
return input, nil
|
|
}
|
|
|
|
func (man *SRouteManager) removeByIface(ctx context.Context, userCred mcclient.TokenCredential, iface *SIface) error {
|
|
routes := []SRoute{}
|
|
q := man.Query().Equals("iface_id", iface.Id)
|
|
if err := db.FetchModelObjects(RouteManager, q, &routes); err != nil {
|
|
return err
|
|
}
|
|
var errs []error
|
|
for j := range routes {
|
|
if err := routes[j].Delete(ctx, userCred); err != nil {
|
|
errs = append(errs, err)
|
|
}
|
|
}
|
|
return errors.NewAggregate(errs)
|
|
}
|
|
|
|
func (man *SRouteManager) getByFilter(filter map[string]string) ([]SRoute, error) {
|
|
routes := []SRoute{}
|
|
q := man.Query()
|
|
for key, val := range filter {
|
|
q = q.Equals(key, val)
|
|
}
|
|
if err := db.FetchModelObjects(RouteManager, q, &routes); err != nil {
|
|
return nil, err
|
|
}
|
|
return routes, nil
|
|
}
|
|
|
|
func (man *SRouteManager) getOneByFilter(filter map[string]string) (*SRoute, error) {
|
|
routes, err := man.getByFilter(filter)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if len(routes) == 0 {
|
|
return nil, errNotFound(fmt.Errorf("cannot find iface route: %#v", filter))
|
|
}
|
|
if len(routes) > 1 {
|
|
return nil, errMoreThanOne(fmt.Errorf("found more than 1 iface routes: %#v", filter))
|
|
}
|
|
return &routes[0], nil
|
|
}
|
|
|
|
func (man *SRouteManager) checkExistenceByFilter(filter map[string]string) error {
|
|
ifaces, err := man.getByFilter(filter)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if len(ifaces) > 0 {
|
|
return fmt.Errorf("iface exist: %s(%s)", ifaces[0].Name, ifaces[0].Id)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (man *SRouteManager) getByIface(iface *SIface) ([]SRoute, error) {
|
|
filter := map[string]string{
|
|
"router_id": iface.RouterId,
|
|
"iface_id": iface.Id,
|
|
}
|
|
routes, err := man.getByFilter(filter)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return routes, nil
|
|
}
|
|
|
|
func (man *SRouteManager) getByRouter(router *SRouter) ([]SRoute, error) {
|
|
filter := map[string]string{
|
|
"router_id": router.Id,
|
|
}
|
|
routes, err := man.getByFilter(filter)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return routes, nil
|
|
}
|
|
|
|
func (route *SRoute) routeLine() string {
|
|
line := route.Network
|
|
if route.Gateway != "" {
|
|
line += " via " + route.Gateway
|
|
}
|
|
if route.Ifname != "" {
|
|
line += " dev " + route.Ifname
|
|
}
|
|
return line
|
|
}
|
|
|
|
func (man *SRouteManager) routeLinesByIface(iface *SIface) ([]string, error) {
|
|
routes, err := man.getByIface(iface)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
lines := []string{}
|
|
for i := range routes {
|
|
route := &routes[i]
|
|
line := route.routeLine()
|
|
if line != "" {
|
|
lines = append(lines, line)
|
|
}
|
|
}
|
|
return lines, nil
|
|
}
|
|
|
|
func (man *SRouteManager) routeLinesRouter(router *SRouter) (map[string][]string, error) {
|
|
routes, err := man.getByRouter(router)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
r := map[string][]string{}
|
|
for i := range routes {
|
|
route := &routes[i]
|
|
line := route.routeLine()
|
|
if line != "" {
|
|
lines := r[route.Ifname]
|
|
lines = append(lines, line)
|
|
r[route.Ifname] = lines
|
|
}
|
|
}
|
|
return r, nil
|
|
}
|