mirror of
https://github.com/yunionio/cloudpods.git
synced 2026-10-29 17:58:36 +08:00
routetables: initial version
This commit is contained in:
@@ -0,0 +1,117 @@
|
||||
package shell
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"yunion.io/x/jsonutils"
|
||||
|
||||
"yunion.io/x/onecloud/pkg/mcclient"
|
||||
"yunion.io/x/onecloud/pkg/mcclient/modules"
|
||||
"yunion.io/x/onecloud/pkg/mcclient/options"
|
||||
)
|
||||
|
||||
func init() {
|
||||
printRouteTableList := func(list *modules.ListResult, columns []string) {
|
||||
data := list.Data
|
||||
for _, jsonObj := range data {
|
||||
jd := jsonObj.(*jsonutils.JSONDict)
|
||||
routesObj, err := jd.GetArray("routes")
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
routes := []string{}
|
||||
for _, routeObj := range routesObj {
|
||||
typ, _ := routeObj.GetString("type")
|
||||
cidr, _ := routeObj.GetString("cidr")
|
||||
next_hop_type, _ := routeObj.GetString("next_hop_type")
|
||||
next_hop, _ := routeObj.GetString("next_hop")
|
||||
route := fmt.Sprintf("%8s: %18s %s", typ, cidr, next_hop_type)
|
||||
if len(next_hop) > 0 {
|
||||
route += fmt.Sprintf(":%s", next_hop)
|
||||
}
|
||||
routes = append(routes, route)
|
||||
}
|
||||
s := strings.Join(routes, "\n")
|
||||
jd.Set("routes", jsonutils.NewString(s))
|
||||
}
|
||||
printList(list, columns)
|
||||
}
|
||||
|
||||
R(&options.RouteTableCreateOptions{}, "routetable-create", "Create routetable", func(s *mcclient.ClientSession, opts *options.RouteTableCreateOptions) error {
|
||||
params, err := opts.Params()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
routetable, err := modules.RouteTables.Create(s, params)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
printObjectRecursive(routetable)
|
||||
return nil
|
||||
})
|
||||
R(&options.RouteTableGetOptions{}, "routetable-show", "Show routetable", func(s *mcclient.ClientSession, opts *options.RouteTableGetOptions) error {
|
||||
routetable, err := modules.RouteTables.Get(s, opts.ID, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
printObjectRecursive(routetable)
|
||||
return nil
|
||||
})
|
||||
R(&options.RouteTableListOptions{}, "routetable-list", "List routetables", func(s *mcclient.ClientSession, opts *options.RouteTableListOptions) error {
|
||||
params, err := options.ListStructToParams(opts)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
result, err := modules.RouteTables.List(s, params)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
printRouteTableList(result, modules.RouteTables.GetColumns(s))
|
||||
return nil
|
||||
})
|
||||
R(&options.RouteTableUpdateOptions{}, "routetable-update", "Update routetable", func(s *mcclient.ClientSession, opts *options.RouteTableUpdateOptions) error {
|
||||
params, err := opts.Params()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
routetable, err := modules.RouteTables.Update(s, opts.ID, params)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
printObjectRecursive(routetable)
|
||||
return nil
|
||||
})
|
||||
R(&options.RouteTableAddRoutesOptions{}, "routetable-add-routes", "Add routes to routetable", func(s *mcclient.ClientSession, opts *options.RouteTableAddRoutesOptions) error {
|
||||
params, err := opts.Params()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
routetable, err := modules.RouteTables.PerformAction(s, opts.ID, "add-routes", params)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
printObjectRecursive(routetable)
|
||||
return nil
|
||||
})
|
||||
R(&options.RouteTableDelRoutesOptions{}, "routetable-del-routes", "Del routes to routetable", func(s *mcclient.ClientSession, opts *options.RouteTableDelRoutesOptions) error {
|
||||
params, err := opts.Params()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
routetable, err := modules.RouteTables.PerformAction(s, opts.ID, "del-routes", params)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
printObjectRecursive(routetable)
|
||||
return nil
|
||||
})
|
||||
R(&options.RouteTableDeleteOptions{}, "routetable-delete", "Show routetable", func(s *mcclient.ClientSession, opts *options.RouteTableDeleteOptions) error {
|
||||
routetable, err := modules.RouteTables.Delete(s, opts.ID, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
printObjectRecursive(routetable)
|
||||
return nil
|
||||
})
|
||||
}
|
||||
@@ -71,6 +71,7 @@ func InitHandlers(app *appsrv.Application) {
|
||||
models.LoadbalancerCertificateManager,
|
||||
models.LoadbalancerAclManager,
|
||||
models.LoadbalancerAgentManager,
|
||||
models.RouteTableManager,
|
||||
|
||||
models.SchedpolicyManager,
|
||||
models.DynamicschedtagManager,
|
||||
|
||||
@@ -0,0 +1,254 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net"
|
||||
"reflect"
|
||||
"strings"
|
||||
|
||||
"yunion.io/x/jsonutils"
|
||||
"yunion.io/x/log"
|
||||
"yunion.io/x/pkg/gotypes"
|
||||
"yunion.io/x/sqlchemy"
|
||||
|
||||
"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 {
|
||||
Type string
|
||||
Cidr string
|
||||
NextHopType string
|
||||
NextHopId string
|
||||
}
|
||||
|
||||
func (route *SRoute) Validate(data *jsonutils.JSONDict) error {
|
||||
if strings.Index(route.Cidr, "/") > 0 {
|
||||
_, ipNet, err := net.ParseCIDR(route.Cidr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// normalize from 192.168.1.3/24 to 192.168.1.0/24
|
||||
route.Cidr = ipNet.String()
|
||||
} else {
|
||||
ip := net.ParseIP(route.Cidr).To4()
|
||||
if ip == nil {
|
||||
return httperrors.NewInputParameterError("invalid addr %s", route.Cidr)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type SRoutes []*SRoute
|
||||
|
||||
func (routes *SRoutes) String() string {
|
||||
return jsonutils.Marshal(routes).String()
|
||||
}
|
||||
func (routes *SRoutes) IsZero() bool {
|
||||
if len([]*SRoute(*routes)) == 0 {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (routes *SRoutes) Validate(data *jsonutils.JSONDict) error {
|
||||
found := map[string]bool{}
|
||||
for _, route := range *routes {
|
||||
if err := route.Validate(data); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, ok := found[route.Cidr]; ok {
|
||||
// error so that the user has a chance to deal with comments
|
||||
return httperrors.NewInputParameterError("duplicate route cidr %s", route.Cidr)
|
||||
}
|
||||
// TODO aliyun: check overlap with System type route
|
||||
found[route.Cidr] = true
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type SRouteTableManager struct {
|
||||
db.SVirtualResourceBaseManager
|
||||
}
|
||||
|
||||
var RouteTableManager *SRouteTableManager
|
||||
|
||||
func init() {
|
||||
gotypes.RegisterSerializable(reflect.TypeOf(&SRoutes{}), func() gotypes.ISerializable {
|
||||
return &SRoutes{}
|
||||
})
|
||||
RouteTableManager = &SRouteTableManager{
|
||||
SVirtualResourceBaseManager: db.NewVirtualResourceBaseManager(
|
||||
SRouteTable{},
|
||||
"route_tables_tbl",
|
||||
"route_table",
|
||||
"route_tables",
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
type SRouteTable struct {
|
||||
db.SVirtualResourceBase
|
||||
|
||||
VpcId string `width:"36" charset:"ascii" nullable:"false" list:"user" create:"required"`
|
||||
CloudregionId string `width:"36" charset:"ascii" nullable:"false" list:"user" create:"optional"`
|
||||
Type string `width:"16" charset:"ascii" nullable:"false" list:"user"`
|
||||
Routes *SRoutes `list:"user" update:"user" create:"required"`
|
||||
}
|
||||
|
||||
func (man *SRouteTableManager) ListItemFilter(ctx context.Context, q *sqlchemy.SQuery, userCred mcclient.TokenCredential, query jsonutils.JSONObject) (*sqlchemy.SQuery, error) {
|
||||
q, err := man.SVirtualResourceBaseManager.ListItemFilter(ctx, q, userCred, query)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
userProjId := userCred.GetProjectId()
|
||||
data := query.(*jsonutils.JSONDict)
|
||||
for _, key := range []string{"vpc", "cloudregion"} {
|
||||
v := validators.NewModelIdOrNameValidator(key, key, userProjId)
|
||||
v.Optional(true)
|
||||
q, err = v.QueryFilter(q, data)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return q, nil
|
||||
}
|
||||
|
||||
func (man *SRouteTableManager) validateRoutes(data *jsonutils.JSONDict, update bool) (*jsonutils.JSONDict, error) {
|
||||
routes := SRoutes{}
|
||||
routesV := validators.NewStructValidator("routes", &routes)
|
||||
if update {
|
||||
routesV.Optional(true)
|
||||
}
|
||||
err := routesV.Validate(data)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return data, nil
|
||||
}
|
||||
|
||||
func (man *SRouteTableManager) ValidateCreateData(ctx context.Context, userCred mcclient.TokenCredential, ownerProjId string, query jsonutils.JSONObject, data *jsonutils.JSONDict) (*jsonutils.JSONDict, error) {
|
||||
data, err := man.validateRoutes(data, false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
vpcV := validators.NewModelIdOrNameValidator("vpc", "vpc", ownerProjId)
|
||||
if err := vpcV.Validate(data); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
vpc := vpcV.Model.(*SVpc)
|
||||
cloudregion := vpc.GetRegion()
|
||||
if cloudregion == nil {
|
||||
return nil, httperrors.NewConflictError("failed fetching cloudregion of vpc %s(%s)", vpc.Name, vpc.Id)
|
||||
}
|
||||
data.Set("cloudregion_id", jsonutils.NewString(cloudregion.Id))
|
||||
return man.SVirtualResourceBaseManager.ValidateCreateData(ctx, userCred, ownerProjId, query, data)
|
||||
}
|
||||
|
||||
func (rt *SRouteTable) ValidateUpdateData(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data *jsonutils.JSONDict) (*jsonutils.JSONDict, error) {
|
||||
data, err := RouteTableManager.validateRoutes(data, true)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return rt.SVirtualResourceBase.ValidateUpdateData(ctx, userCred, query, data)
|
||||
}
|
||||
|
||||
func (rt *SRouteTable) AllowPerformAddRoutes(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data *jsonutils.JSONDict) bool {
|
||||
return rt.IsOwner(userCred) || userCred.IsSystemAdmin()
|
||||
}
|
||||
|
||||
func (rt *SRouteTable) AllowPerformDelRoutes(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data *jsonutils.JSONDict) bool {
|
||||
return rt.AllowPerformAddRoutes(ctx, userCred, query, data)
|
||||
}
|
||||
|
||||
// PerformAddRoutes patches acl entries by adding then deleting the specified acls.
|
||||
// This is intended mainly for command line operations.
|
||||
func (rt *SRouteTable) PerformAddRoutes(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data *jsonutils.JSONDict) (*jsonutils.JSONDict, error) {
|
||||
routes := gotypes.DeepCopy(rt.Routes).(SRoutes)
|
||||
{
|
||||
adds := SRoutes{}
|
||||
addsV := validators.NewStructValidator("routes", &adds)
|
||||
addsV.Optional(true)
|
||||
err := addsV.Validate(data)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, add := range adds {
|
||||
found := false
|
||||
for _, route := range routes {
|
||||
if route.Cidr == add.Cidr {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
routes = append(routes, add)
|
||||
}
|
||||
}
|
||||
}
|
||||
_, err := rt.GetModelManager().TableSpec().Update(rt, func() error {
|
||||
rt.Routes = &routes
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (rt *SRouteTable) PerformDelRoutes(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data *jsonutils.JSONDict) (*jsonutils.JSONDict, error) {
|
||||
routes := gotypes.DeepCopy(rt.Routes).(SRoutes)
|
||||
{
|
||||
cidrs := []string{}
|
||||
err := data.Unmarshal(&cidrs, "cidrs")
|
||||
if err != nil {
|
||||
return nil, httperrors.NewInputParameterError("unmarshaling cidrs failed: %s", err)
|
||||
}
|
||||
for _, cidr := range cidrs {
|
||||
for i := len(routes) - 1; i >= 0; i-- {
|
||||
route := routes[i]
|
||||
if route.Type == "system" {
|
||||
continue
|
||||
}
|
||||
if route.Cidr == cidr {
|
||||
routes = append(routes[:i], routes[i+1:]...)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
_, err := rt.GetModelManager().TableSpec().Update(rt, func() error {
|
||||
rt.Routes = &routes
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (rt *SRouteTable) GetCustomizeColumns(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) *jsonutils.JSONDict {
|
||||
extra := rt.SVirtualResourceBase.GetCustomizeColumns(ctx, userCred, query)
|
||||
vpcM, err := VpcManager.FetchById(rt.VpcId)
|
||||
if err != nil {
|
||||
log.Errorf("route table %s(%s): fetch vpc (%s) error: %s",
|
||||
rt.Name, rt.Id, rt.VpcId, err)
|
||||
return extra
|
||||
}
|
||||
cloudregionM, err := CloudregionManager.FetchById(rt.CloudregionId)
|
||||
if err != nil {
|
||||
log.Errorf("route table %s(%s): fetch cloud region (%s) error: %s",
|
||||
rt.Name, rt.Id, rt.CloudregionId, err)
|
||||
return extra
|
||||
}
|
||||
extra.Set("vpc", jsonutils.NewString(vpcM.GetName()))
|
||||
extra.Set("cloudregion", jsonutils.NewString(cloudregionM.GetName()))
|
||||
return extra
|
||||
}
|
||||
|
||||
func (rt *SRouteTable) GetExtraDetails(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) *jsonutils.JSONDict {
|
||||
extra := rt.GetCustomizeColumns(ctx, userCred, query)
|
||||
return extra
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package modules
|
||||
|
||||
type RouteTableManager struct {
|
||||
ResourceManager
|
||||
}
|
||||
|
||||
var (
|
||||
RouteTables RouteTableManager
|
||||
)
|
||||
|
||||
func init() {
|
||||
RouteTables = RouteTableManager{
|
||||
NewComputeManager(
|
||||
"route_table",
|
||||
"route_tables",
|
||||
[]string{
|
||||
"id",
|
||||
"name",
|
||||
"type",
|
||||
"vpc",
|
||||
"vpc_id",
|
||||
"routes",
|
||||
},
|
||||
[]string{"tenant"},
|
||||
),
|
||||
}
|
||||
registerCompute(&RouteTables)
|
||||
}
|
||||
@@ -0,0 +1,133 @@
|
||||
package options
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"yunion.io/x/jsonutils"
|
||||
)
|
||||
|
||||
type Route struct {
|
||||
Type string
|
||||
Cidr string
|
||||
NextHopType string
|
||||
NextHopId string
|
||||
}
|
||||
|
||||
type Routes []*Route
|
||||
|
||||
type RoutesOptions struct {
|
||||
RouteType []string
|
||||
RouteCidr []string
|
||||
RouteNextHopType []string
|
||||
RouteNextHopId []string
|
||||
}
|
||||
|
||||
func (opts *RoutesOptions) Params() (jsonutils.JSONObject, error) {
|
||||
len0 := len(opts.RouteType)
|
||||
len1 := len(opts.RouteCidr)
|
||||
if len0 != len1 || len0 != len(opts.RouteNextHopType) || len1 != len(opts.RouteNextHopId) {
|
||||
return nil, fmt.Errorf("there must be equal number of options of --route-xxx")
|
||||
}
|
||||
routes := []*Route{}
|
||||
for i := 0; i < len0; i++ {
|
||||
routes = append(routes, &Route{
|
||||
Type: opts.RouteType[i],
|
||||
Cidr: opts.RouteCidr[i],
|
||||
NextHopType: opts.RouteNextHopType[i],
|
||||
NextHopId: opts.RouteNextHopId[i],
|
||||
})
|
||||
}
|
||||
routesJson := jsonutils.Marshal(routes)
|
||||
return routesJson, nil
|
||||
}
|
||||
|
||||
type RouteTableCreateOptions struct {
|
||||
NAME string
|
||||
Vpc string
|
||||
|
||||
RoutesOptions
|
||||
}
|
||||
|
||||
func (opts *RouteTableCreateOptions) Params() (*jsonutils.JSONDict, error) {
|
||||
params, err := optionsStructToParams(opts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
routesJson, err := opts.RoutesOptions.Params()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
params.Set("routes", routesJson)
|
||||
return params, nil
|
||||
}
|
||||
|
||||
type RouteTableGetOptions struct {
|
||||
ID string
|
||||
}
|
||||
|
||||
type RouteTableUpdateOptions struct {
|
||||
ID string `json:"-"`
|
||||
Name string
|
||||
|
||||
RoutesOptions
|
||||
}
|
||||
|
||||
func (opts *RouteTableUpdateOptions) Params() (*jsonutils.JSONDict, error) {
|
||||
params, err := optionsStructToParams(opts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(opts.RouteCidr) != 0 {
|
||||
routesJson, err := opts.RoutesOptions.Params()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
params.Set("routes", routesJson)
|
||||
}
|
||||
return params, nil
|
||||
}
|
||||
|
||||
type RouteTableAddRoutesOptions struct {
|
||||
ID string `json:"-"`
|
||||
|
||||
RoutesOptions
|
||||
}
|
||||
|
||||
func (opts *RouteTableAddRoutesOptions) Params() (*jsonutils.JSONDict, error) {
|
||||
if len(opts.RouteCidr) == 0 {
|
||||
return nil, fmt.Errorf("nothing to add")
|
||||
}
|
||||
routesJson, err := opts.RoutesOptions.Params()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
params := jsonutils.NewDict()
|
||||
params.Set("routes", routesJson)
|
||||
return params, nil
|
||||
}
|
||||
|
||||
type RouteTableDelRoutesOptions struct {
|
||||
ID string `json:"-"`
|
||||
|
||||
RouteCidr []string
|
||||
}
|
||||
|
||||
func (opts *RouteTableDelRoutesOptions) Params() (*jsonutils.JSONDict, error) {
|
||||
if len(opts.RouteCidr) == 0 {
|
||||
return nil, fmt.Errorf("nothing to del")
|
||||
}
|
||||
params := jsonutils.NewDict()
|
||||
params.Set("cidrs", jsonutils.Marshal(opts.RouteCidr))
|
||||
return params, nil
|
||||
}
|
||||
|
||||
type RouteTableDeleteOptions struct {
|
||||
ID string
|
||||
}
|
||||
|
||||
type RouteTableListOptions struct {
|
||||
Vpc string
|
||||
Cloudregion string
|
||||
|
||||
BaseListOptions
|
||||
}
|
||||
Reference in New Issue
Block a user