mirror of
https://github.com/yunionio/cloudpods.git
synced 2026-08-29 03:51:54 +08:00
cloudnet: initial version
This commit is contained in:
@@ -0,0 +1,10 @@
|
||||
region = 'Yunion'
|
||||
address = '10.168.222.136'
|
||||
port = 8891
|
||||
auth_uri = 'http://10.168.222.136:35357/v3'
|
||||
admin_user = 'cloudnetadmin'
|
||||
admin_password = 'xxxxxxxxxxxxxxxx'
|
||||
admin_tenant_name = 'system'
|
||||
sql_connection = 'mysql+pymysql://yunionansible:9YuQL9KEZn1mppaK@localhost:3306/yunionansible?charset=utf8'
|
||||
|
||||
auto_sync_table = True
|
||||
@@ -0,0 +1 @@
|
||||
DESCRIPTION="Yunion Cloudnet"
|
||||
@@ -32,6 +32,7 @@ import (
|
||||
|
||||
"yunion.io/x/onecloud/cmd/climc/promputils"
|
||||
"yunion.io/x/onecloud/cmd/climc/shell"
|
||||
_ "yunion.io/x/onecloud/cmd/climc/shell/cloudnet"
|
||||
_ "yunion.io/x/onecloud/cmd/climc/shell/etcd"
|
||||
_ "yunion.io/x/onecloud/cmd/climc/shell/k8s"
|
||||
"yunion.io/x/onecloud/pkg/mcclient"
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
package cloudnet
|
||||
|
||||
import (
|
||||
"yunion.io/x/onecloud/cmd/climc/shell"
|
||||
"yunion.io/x/onecloud/pkg/util/printutils"
|
||||
)
|
||||
|
||||
var (
|
||||
R = shell.R
|
||||
printList = printutils.PrintJSONList
|
||||
printObject = printutils.PrintJSONObject
|
||||
printBatchResults = printutils.PrintJSONBatchResults
|
||||
)
|
||||
@@ -0,0 +1,82 @@
|
||||
// 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 cloudnet
|
||||
|
||||
import (
|
||||
"yunion.io/x/onecloud/pkg/mcclient"
|
||||
modules "yunion.io/x/onecloud/pkg/mcclient/modules/cloudnet"
|
||||
base_options "yunion.io/x/onecloud/pkg/mcclient/options"
|
||||
options "yunion.io/x/onecloud/pkg/mcclient/options/cloudnet"
|
||||
)
|
||||
|
||||
func init() {
|
||||
R(&options.MeshNetworkCreateOptions{}, "meshnetwork-create", "Create mesh network", func(s *mcclient.ClientSession, opts *options.MeshNetworkCreateOptions) error {
|
||||
params, err := base_options.StructToParams(opts)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
mn, err := modules.MeshNetworks.Create(s, params)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
printObject(mn)
|
||||
return nil
|
||||
})
|
||||
R(&options.MeshNetworkGetOptions{}, "meshnetwork-show", "Show mesh network", func(s *mcclient.ClientSession, opts *options.MeshNetworkGetOptions) error {
|
||||
mn, err := modules.MeshNetworks.Get(s, opts.ID, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
printObject(mn)
|
||||
return nil
|
||||
})
|
||||
R(&options.MeshNetworkListOptions{}, "meshnetwork-list", "List mesh networks", func(s *mcclient.ClientSession, opts *options.MeshNetworkListOptions) error {
|
||||
params, err := base_options.ListStructToParams(opts)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
result, err := modules.MeshNetworks.List(s, params)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
printList(result, modules.MeshNetworks.GetColumns(s))
|
||||
return nil
|
||||
})
|
||||
R(&options.MeshNetworkUpdateOptions{}, "meshnetwork-update", "Update mesh network", func(s *mcclient.ClientSession, opts *options.MeshNetworkUpdateOptions) error {
|
||||
params, err := base_options.StructToParams(opts)
|
||||
mn, err := modules.MeshNetworks.Update(s, opts.ID, params)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
printObject(mn)
|
||||
return nil
|
||||
})
|
||||
R(&options.MeshNetworkDeleteOptions{}, "meshnetwork-delete", "Delete mesh network", func(s *mcclient.ClientSession, opts *options.MeshNetworkDeleteOptions) error {
|
||||
mn, err := modules.MeshNetworks.Delete(s, opts.ID, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
printObject(mn)
|
||||
return nil
|
||||
})
|
||||
R(&options.MeshNetworkActionRealizeOptions{}, "meshnetwork-realize", "Realize mesh network", func(s *mcclient.ClientSession, opts *options.MeshNetworkActionRealizeOptions) error {
|
||||
mn, err := modules.MeshNetworks.PerformAction(s, opts.ID, "realize", nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
printObject(mn)
|
||||
return nil
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,134 @@
|
||||
// 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 cloudnet
|
||||
|
||||
import (
|
||||
"yunion.io/x/onecloud/pkg/mcclient"
|
||||
modules "yunion.io/x/onecloud/pkg/mcclient/modules/cloudnet"
|
||||
base_options "yunion.io/x/onecloud/pkg/mcclient/options"
|
||||
options "yunion.io/x/onecloud/pkg/mcclient/options/cloudnet"
|
||||
)
|
||||
|
||||
func init() {
|
||||
R(&options.RouterCreateOptions{}, "router-create", "Create router", func(s *mcclient.ClientSession, opts *options.RouterCreateOptions) error {
|
||||
params, err := opts.Params()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
router, err := modules.Routers.Create(s, params)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
printObject(router)
|
||||
return nil
|
||||
})
|
||||
R(&options.RouterGetOptions{}, "router-show", "Show router", func(s *mcclient.ClientSession, opts *options.RouterGetOptions) error {
|
||||
router, err := modules.Routers.Get(s, opts.ID, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
printObject(router)
|
||||
return nil
|
||||
})
|
||||
R(&options.RouterListOptions{}, "router-list", "List routers", func(s *mcclient.ClientSession, opts *options.RouterListOptions) error {
|
||||
params, err := base_options.ListStructToParams(opts)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
result, err := modules.Routers.List(s, params)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
printList(result, modules.Routers.GetColumns(s))
|
||||
return nil
|
||||
})
|
||||
R(&options.RouterUpdateOptions{}, "router-update", "Update router", func(s *mcclient.ClientSession, opts *options.RouterUpdateOptions) error {
|
||||
params, err := base_options.StructToParams(opts)
|
||||
router, err := modules.Routers.Update(s, opts.ID, params)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
printObject(router)
|
||||
return nil
|
||||
})
|
||||
R(&options.RouterDeleteOptions{}, "router-delete", "Delete router", func(s *mcclient.ClientSession, opts *options.RouterDeleteOptions) error {
|
||||
router, err := modules.Routers.Delete(s, opts.ID, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
printObject(router)
|
||||
return nil
|
||||
})
|
||||
R(&options.RouterActionJoinMeshNetworkOptions{}, "router-join-meshnetwork", "Router join meshnetwork", func(s *mcclient.ClientSession, opts *options.RouterActionJoinMeshNetworkOptions) error {
|
||||
params, err := base_options.StructToParams(opts)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
router, err := modules.Routers.PerformAction(s, opts.ID, "join-mesh-network", params)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
printObject(router)
|
||||
return nil
|
||||
})
|
||||
R(&options.RouterActionLeaveMeshNetworkOptions{}, "router-leave-meshnetwork", "Router leave meshnetwork", func(s *mcclient.ClientSession, opts *options.RouterActionLeaveMeshNetworkOptions) error {
|
||||
params, err := base_options.StructToParams(opts)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
router, err := modules.Routers.PerformAction(s, opts.ID, "leave-mesh-network", params)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
printObject(router)
|
||||
return nil
|
||||
})
|
||||
R(&options.RouterActionRegisterIfnameOptions{}, "router-register-ifname", "Router register new ifname", func(s *mcclient.ClientSession, opts *options.RouterActionRegisterIfnameOptions) error {
|
||||
params, err := base_options.StructToParams(opts)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
router, err := modules.Routers.PerformAction(s, opts.ID, "register-ifname", params)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
printObject(router)
|
||||
return nil
|
||||
})
|
||||
R(&options.RouterActionUnregisterIfnameOptions{}, "router-unregister-ifname", "Router unregister ifname", func(s *mcclient.ClientSession, opts *options.RouterActionUnregisterIfnameOptions) error {
|
||||
params, err := base_options.StructToParams(opts)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
router, err := modules.Routers.PerformAction(s, opts.ID, "unregister-ifname", params)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
printObject(router)
|
||||
return nil
|
||||
})
|
||||
R(&options.RouterActionRealizeOptions{}, "router-realize", "Router realize", func(s *mcclient.ClientSession, opts *options.RouterActionRealizeOptions) error {
|
||||
params, err := base_options.StructToParams(opts)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
router, err := modules.Routers.PerformAction(s, opts.ID, "realize", params)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
printObject(router)
|
||||
return nil
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
// 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 cloudnet
|
||||
|
||||
import (
|
||||
"yunion.io/x/onecloud/pkg/mcclient"
|
||||
modules "yunion.io/x/onecloud/pkg/mcclient/modules/cloudnet"
|
||||
base_options "yunion.io/x/onecloud/pkg/mcclient/options"
|
||||
options "yunion.io/x/onecloud/pkg/mcclient/options/cloudnet"
|
||||
)
|
||||
|
||||
func init() {
|
||||
R(&options.RouteCreateOptions{}, "router-route-create", "Create router", func(s *mcclient.ClientSession, opts *options.RouteCreateOptions) error {
|
||||
params, err := base_options.StructToParams(opts)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
router, err := modules.Routes.Create(s, params)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
printObject(router)
|
||||
return nil
|
||||
})
|
||||
R(&options.RouteGetOptions{}, "router-route-show", "Show router", func(s *mcclient.ClientSession, opts *options.RouteGetOptions) error {
|
||||
router, err := modules.Routes.Get(s, opts.ID, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
printObject(router)
|
||||
return nil
|
||||
})
|
||||
R(&options.RouteListOptions{}, "router-route-list", "List routers", func(s *mcclient.ClientSession, opts *options.RouteListOptions) error {
|
||||
params, err := base_options.ListStructToParams(opts)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
result, err := modules.Routes.List(s, params)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
printList(result, modules.Routes.GetColumns(s))
|
||||
return nil
|
||||
})
|
||||
R(&options.RouteUpdateOptions{}, "router-route-update", "Update router", func(s *mcclient.ClientSession, opts *options.RouteUpdateOptions) error {
|
||||
params, err := base_options.StructToParams(opts)
|
||||
router, err := modules.Routes.Update(s, opts.ID, params)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
printObject(router)
|
||||
return nil
|
||||
})
|
||||
R(&options.RouteDeleteOptions{}, "router-route-delete", "Delete router", func(s *mcclient.ClientSession, opts *options.RouteDeleteOptions) error {
|
||||
router, err := modules.Routes.Delete(s, opts.ID, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
printObject(router)
|
||||
return nil
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
// 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 cloudnet
|
||||
|
||||
import (
|
||||
"yunion.io/x/onecloud/pkg/mcclient"
|
||||
modules "yunion.io/x/onecloud/pkg/mcclient/modules/cloudnet"
|
||||
base_options "yunion.io/x/onecloud/pkg/mcclient/options"
|
||||
options "yunion.io/x/onecloud/pkg/mcclient/options/cloudnet"
|
||||
)
|
||||
|
||||
func init() {
|
||||
R(&options.RuleCreateOptions{}, "router-rule-create", "Create router rule", func(s *mcclient.ClientSession, opts *options.RuleCreateOptions) error {
|
||||
params, err := base_options.StructToParams(opts)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
router, err := modules.Rules.Create(s, params)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
printObject(router)
|
||||
return nil
|
||||
})
|
||||
R(&options.RuleGetOptions{}, "router-rule-show", "Show router rule", func(s *mcclient.ClientSession, opts *options.RuleGetOptions) error {
|
||||
router, err := modules.Rules.Get(s, opts.ID, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
printObject(router)
|
||||
return nil
|
||||
})
|
||||
R(&options.RuleListOptions{}, "router-rule-list", "List router rules", func(s *mcclient.ClientSession, opts *options.RuleListOptions) error {
|
||||
params, err := base_options.ListStructToParams(opts)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
result, err := modules.Rules.List(s, params)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
printList(result, modules.Rules.GetColumns(s))
|
||||
return nil
|
||||
})
|
||||
R(&options.RuleUpdateOptions{}, "router-rule-update", "Update router rule", func(s *mcclient.ClientSession, opts *options.RuleUpdateOptions) error {
|
||||
params, err := base_options.StructToParams(opts)
|
||||
router, err := modules.Rules.Update(s, opts.ID, params)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
printObject(router)
|
||||
return nil
|
||||
})
|
||||
R(&options.RuleDeleteOptions{}, "router-rule-delete", "Delete router rule", func(s *mcclient.ClientSession, opts *options.RuleDeleteOptions) error {
|
||||
router, err := modules.Rules.Delete(s, opts.ID, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
printObject(router)
|
||||
return nil
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
// 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 (
|
||||
"yunion.io/x/onecloud/pkg/cloudnet/service"
|
||||
)
|
||||
|
||||
func main() {
|
||||
service.StartService()
|
||||
}
|
||||
@@ -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/cloudnet/models"
|
||||
@@ -0,0 +1,212 @@
|
||||
// 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"
|
||||
"net"
|
||||
"strings"
|
||||
|
||||
"yunion.io/x/log"
|
||||
yerrors "yunion.io/x/pkg/util/errors"
|
||||
"yunion.io/x/pkg/util/netutils"
|
||||
|
||||
"yunion.io/x/onecloud/pkg/cloudcommon/db"
|
||||
"yunion.io/x/onecloud/pkg/mcclient"
|
||||
)
|
||||
|
||||
type SIfacePeer struct {
|
||||
db.SStandaloneResourceBase
|
||||
|
||||
RouterId string
|
||||
IfaceId string
|
||||
|
||||
PeerIfaceId string
|
||||
PeerRouterId string
|
||||
|
||||
PublicKey string
|
||||
AllowedIPs string
|
||||
Endpoint string
|
||||
PersistentKeepalive int
|
||||
}
|
||||
|
||||
type SIfacePeerManager struct {
|
||||
db.SStandaloneResourceBaseManager
|
||||
}
|
||||
|
||||
var IfacePeerManager *SIfacePeerManager
|
||||
|
||||
func init() {
|
||||
IfacePeerManager = &SIfacePeerManager{
|
||||
SStandaloneResourceBaseManager: db.NewStandaloneResourceBaseManager(
|
||||
SIfacePeer{},
|
||||
"ifacepeers_tbl",
|
||||
"ifacepeer",
|
||||
"ifacepeers",
|
||||
),
|
||||
}
|
||||
IfacePeerManager.SetVirtualObject(IfacePeerManager)
|
||||
}
|
||||
|
||||
func (ifacePeer *SIfacePeer) subnetsStrList() []string {
|
||||
return strings.Split(ifacePeer.AllowedIPs, ",")
|
||||
}
|
||||
|
||||
func (ifacePeer *SIfacePeer) subnetsParsed() Subnets {
|
||||
parts := ifacePeer.subnetsStrList()
|
||||
r := make([]*netutils.IPV4Prefix, 0, len(parts))
|
||||
for _, part := range parts {
|
||||
p, err := netutils.NewIPV4Prefix(part)
|
||||
if err != nil {
|
||||
log.Errorf("%s: invalid subnet sneaked in: %s", ifacePeer.Id, part)
|
||||
return nil
|
||||
}
|
||||
r = append(r, &p)
|
||||
}
|
||||
return Subnets(r)
|
||||
}
|
||||
|
||||
func (man *SIfacePeerManager) removeByPeerIface(ctx context.Context, userCred mcclient.TokenCredential, iface *SIface) error {
|
||||
peers := []SIfacePeer{}
|
||||
q := man.Query().Equals("peer_iface_id", iface.Id)
|
||||
if err := db.FetchModelObjects(IfacePeerManager, q, &peers); err != nil {
|
||||
return err
|
||||
}
|
||||
var errs []error
|
||||
for j := range peers {
|
||||
if err := peers[j].Delete(ctx, userCred); err != nil {
|
||||
errs = append(errs, err)
|
||||
}
|
||||
}
|
||||
return yerrors.NewAggregate(errs)
|
||||
}
|
||||
|
||||
func (man *SIfacePeerManager) removeByIface(ctx context.Context, userCred mcclient.TokenCredential, iface *SIface) error {
|
||||
peers := []SIfacePeer{}
|
||||
q := man.Query().Equals("iface_id", iface.Id)
|
||||
if err := db.FetchModelObjects(IfacePeerManager, q, &peers); err != nil {
|
||||
return err
|
||||
}
|
||||
var errs []error
|
||||
for j := range peers {
|
||||
if err := peers[j].Delete(ctx, userCred); err != nil {
|
||||
errs = append(errs, err)
|
||||
}
|
||||
}
|
||||
return yerrors.NewAggregate(errs)
|
||||
}
|
||||
|
||||
func (man *SIfacePeerManager) getByFilter(filter map[string]string) ([]SIfacePeer, error) {
|
||||
ifacePeers := []SIfacePeer{}
|
||||
q := man.Query()
|
||||
for key, val := range filter {
|
||||
q = q.Equals(key, val)
|
||||
}
|
||||
if err := db.FetchModelObjects(IfacePeerManager, q, &ifacePeers); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return ifacePeers, nil
|
||||
}
|
||||
|
||||
func (man *SIfacePeerManager) getOneByFilter(filter map[string]string) (*SIfacePeer, error) {
|
||||
ifacePeers, err := man.getByFilter(filter)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(ifacePeers) == 0 {
|
||||
return nil, errNotFound(fmt.Errorf("cannot find iface peer: %#v", filter))
|
||||
}
|
||||
if len(ifacePeers) > 1 {
|
||||
return nil, errMoreThanOne(fmt.Errorf("found more than 1 iface peers: %#v", filter))
|
||||
}
|
||||
return &ifacePeers[0], nil
|
||||
}
|
||||
|
||||
func (man *SIfacePeerManager) getByIface(iface *SIface) ([]SIfacePeer, error) {
|
||||
filter := map[string]string{
|
||||
"router_id": iface.RouterId,
|
||||
"iface_id": iface.Id,
|
||||
}
|
||||
ifacePeers, err := man.getByFilter(filter)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return ifacePeers, nil
|
||||
}
|
||||
|
||||
func (man *SIfacePeerManager) getByIfacePublicKey(iface *SIface, pubkey string) (*SIfacePeer, error) {
|
||||
filter := map[string]string{
|
||||
"router_id": iface.RouterId,
|
||||
"iface_id": iface.Id,
|
||||
"public_key": pubkey,
|
||||
}
|
||||
ifacePeer, err := man.getOneByFilter(filter)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return ifacePeer, nil
|
||||
}
|
||||
|
||||
func (man *SIfacePeerManager) checkAllowedIPs(iface *SIface, oldPeer *SIfacePeer, allowedNets Subnets) error {
|
||||
ifacePeers, err := man.getByIface(iface)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for i := range ifacePeers {
|
||||
ifacePeer := &ifacePeers[i]
|
||||
if oldPeer != nil && oldPeer.Id == ifacePeer.Id {
|
||||
continue
|
||||
}
|
||||
existingNets := ifacePeer.subnetsParsed()
|
||||
if _, net := existingNets.ContainsAnyEx(allowedNets); net != nil {
|
||||
return fmt.Errorf("subnet %s is already occupied by peer %s(%s)",
|
||||
net, ifacePeer.Name, ifacePeer.Id)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (man *SIfacePeerManager) updateEndpointIPByPeerRouter(ctx context.Context, endpointIP string, router *SRouter) error {
|
||||
filter := map[string]string{
|
||||
"peer_router_id": router.Id,
|
||||
}
|
||||
ifacePeers, err := man.getByFilter(filter)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var errs []error
|
||||
for i := range ifacePeers {
|
||||
ifacePeer := &ifacePeers[i]
|
||||
host, port, err := net.SplitHostPort(ifacePeer.Endpoint)
|
||||
if err != nil {
|
||||
errs = append(errs, err)
|
||||
continue
|
||||
}
|
||||
if host == endpointIP {
|
||||
continue
|
||||
}
|
||||
_, err = db.Update(ifacePeer, func() error {
|
||||
endpoint := net.JoinHostPort(endpointIP, port)
|
||||
ifacePeer.Endpoint = endpoint
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
errs = append(errs, err)
|
||||
}
|
||||
}
|
||||
return yerrors.NewAggregate(errs)
|
||||
}
|
||||
@@ -0,0 +1,420 @@
|
||||
// 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"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
|
||||
"yunion.io/x/jsonutils"
|
||||
yerrors "yunion.io/x/pkg/util/errors"
|
||||
"yunion.io/x/sqlchemy"
|
||||
|
||||
"yunion.io/x/onecloud/pkg/cloudcommon/db"
|
||||
"yunion.io/x/onecloud/pkg/cloudcommon/validators"
|
||||
cnutils "yunion.io/x/onecloud/pkg/cloudnet/utils"
|
||||
"yunion.io/x/onecloud/pkg/mcclient"
|
||||
"yunion.io/x/pkg/util/netutils"
|
||||
)
|
||||
|
||||
var regexpIfname = regexp.MustCompile(`[A-Za-z][A-Za-z0-9]{0,14}`)
|
||||
|
||||
type SIface struct {
|
||||
db.SStandaloneResourceBase
|
||||
|
||||
RouterId string `length:"32" nullable:"false"`
|
||||
NetworkId string `length:"32" nullable:"false"`
|
||||
|
||||
Ifname string `length:"32" nullable:"false"`
|
||||
|
||||
PrivateKey string
|
||||
PublicKey string
|
||||
ListenPort int `nullable:"false"`
|
||||
|
||||
IsSystem bool `nullable:"false"`
|
||||
}
|
||||
|
||||
type SIfaceManager struct {
|
||||
db.SStandaloneResourceBaseManager
|
||||
}
|
||||
|
||||
var IfaceManager *SIfaceManager
|
||||
|
||||
func init() {
|
||||
IfaceManager = &SIfaceManager{
|
||||
SStandaloneResourceBaseManager: db.NewStandaloneResourceBaseManager(
|
||||
SIface{},
|
||||
"ifaces_tbl",
|
||||
"iface",
|
||||
"ifaces",
|
||||
),
|
||||
}
|
||||
IfaceManager.SetVirtualObject(IfaceManager)
|
||||
}
|
||||
|
||||
func (man *SIfaceManager) ValidateCreateData(ctx context.Context, userCred mcclient.TokenCredential, ownerId mcclient.IIdentityProvider, query jsonutils.JSONObject, data *jsonutils.JSONDict) (*jsonutils.JSONDict, error) {
|
||||
// router existence
|
||||
// PrivateKey validation , or generation
|
||||
// ListenPort validation, uniqueness
|
||||
// ListenPort generation
|
||||
return nil, errors.New("manually adding interface is currently not supported")
|
||||
}
|
||||
|
||||
func (man *SIfaceManager) ListItemFilter(ctx context.Context, q *sqlchemy.SQuery, userCred mcclient.TokenCredential, query jsonutils.JSONObject) (*sqlchemy.SQuery, error) {
|
||||
q, err := man.SStandaloneResourceBaseManager.ListItemFilter(ctx, q, userCred, query)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
data := query.(*jsonutils.JSONDict)
|
||||
q, err = validators.ApplyModelFilters(q, data, []*validators.ModelFilterOptions{
|
||||
{Key: "router", ModelKeyword: "router", OwnerId: userCred},
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return q, nil
|
||||
}
|
||||
|
||||
func (iface *SIface) ValidateUpdateCondition(ctx context.Context) error {
|
||||
// same as create but no generation
|
||||
// if privatekey updated
|
||||
// update peers whose peerifaceid == self.id
|
||||
return nil
|
||||
}
|
||||
|
||||
func (iface *SIface) ValidateDeleteCondition(ctx context.Context) error {
|
||||
// if networkid != "" {
|
||||
// return errors.New("part of network, remove it by remove network memeber")
|
||||
// }
|
||||
return nil
|
||||
}
|
||||
|
||||
func (iface *SIface) CustomizeDelete(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) error {
|
||||
// remove ifacepeer whose peerifaceId is self.id
|
||||
return nil
|
||||
}
|
||||
|
||||
func (iface *SIface) addOrUpdatePeer(ctx context.Context, userCred mcclient.TokenCredential,
|
||||
peerIface *SIface, allowedNets Subnets, peerRouter *SRouter) error {
|
||||
// XXX lock
|
||||
endpoint := ""
|
||||
endpointIP := peerRouter.endpointIP()
|
||||
if endpointIP != "" && peerIface.ListenPort > 0 {
|
||||
endpoint = fmt.Sprintf("%s:%d", endpointIP, peerIface.ListenPort)
|
||||
}
|
||||
persistentKeepalive := 0
|
||||
if endpointIP != "" {
|
||||
// persistent keepalive from private addr to exit addr
|
||||
router, err := iface.getRouter()
|
||||
if err != nil {
|
||||
return errors.WithMessagef(err, "get iface router %s", iface.RouterId)
|
||||
}
|
||||
myIP := router.endpointIP()
|
||||
if myIP != "" {
|
||||
myIPAddr, err := netutils.NewIPV4Addr(myIP)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if netutils.IsPrivate(myIPAddr) {
|
||||
peerIPAddr, err := netutils.NewIPV4Addr(endpointIP)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if netutils.IsExitAddress(peerIPAddr) {
|
||||
persistentKeepalive = 10
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
ifacePeer, err := IfacePeerManager.getByIfacePublicKey(iface, peerIface.PublicKey)
|
||||
if err != nil && !IsNotFound(err) {
|
||||
return err
|
||||
}
|
||||
if err := IfacePeerManager.checkAllowedIPs(iface, ifacePeer, allowedNets); err != nil {
|
||||
return err
|
||||
}
|
||||
if ifacePeer == nil {
|
||||
ifacePeer := &SIfacePeer{
|
||||
RouterId: iface.RouterId,
|
||||
IfaceId: iface.Id,
|
||||
|
||||
PeerIfaceId: peerIface.Id,
|
||||
PeerRouterId: peerIface.RouterId,
|
||||
PublicKey: peerIface.PublicKey,
|
||||
AllowedIPs: allowedNets.String(),
|
||||
Endpoint: endpoint,
|
||||
PersistentKeepalive: persistentKeepalive,
|
||||
}
|
||||
ifacePeer.Name = fmt.Sprintf("%s-%s", iface.Name, peerIface.Name)
|
||||
err := IfacePeerManager.TableSpec().Insert(ifacePeer)
|
||||
return err
|
||||
}
|
||||
_, err = db.Update(ifacePeer, func() error {
|
||||
ifacePeer.PeerIfaceId = peerIface.Id
|
||||
ifacePeer.PeerRouterId = peerIface.RouterId
|
||||
ifacePeer.Endpoint = endpoint
|
||||
ifacePeer.AllowedIPs = allowedNets.String()
|
||||
ifacePeer.PersistentKeepalive = persistentKeepalive
|
||||
return nil
|
||||
})
|
||||
return err
|
||||
}
|
||||
|
||||
func (iface *SIface) clearPeers(ctx context.Context, userCred mcclient.TokenCredential) error {
|
||||
return IfacePeerManager.removeByIface(ctx, userCred, iface)
|
||||
}
|
||||
|
||||
func (iface *SIface) clearPeerRefs(ctx context.Context, userCred mcclient.TokenCredential) error {
|
||||
return IfacePeerManager.removeByPeerIface(ctx, userCred, iface)
|
||||
}
|
||||
|
||||
func (iface *SIface) clearRoutes(ctx context.Context, userCred mcclient.TokenCredential) error {
|
||||
return RouteManager.removeByIface(ctx, userCred, iface)
|
||||
}
|
||||
|
||||
func (iface *SIface) remove(ctx context.Context, userCred mcclient.TokenCredential) error {
|
||||
var errs []error
|
||||
if err := iface.clearRoutes(ctx, userCred); err != nil {
|
||||
errs = append(errs, err)
|
||||
}
|
||||
if err := iface.clearPeers(ctx, userCred); err != nil {
|
||||
errs = append(errs, err)
|
||||
}
|
||||
if err := iface.clearPeerRefs(ctx, userCred); err != nil {
|
||||
errs = append(errs, err)
|
||||
}
|
||||
if len(errs) == 0 {
|
||||
if err := iface.Delete(ctx, userCred); err != nil {
|
||||
errs = append(errs, err)
|
||||
}
|
||||
}
|
||||
return yerrors.NewAggregate(errs)
|
||||
}
|
||||
|
||||
func (iface *SIface) isTypeWireguard() bool {
|
||||
r := iface.ListenPort > 0 && iface.PrivateKey != "" && iface.PublicKey != ""
|
||||
return r
|
||||
}
|
||||
|
||||
func (iface *SIface) getRouter() (*SRouter, error) {
|
||||
obj, err := db.FetchById(RouterManager, iface.RouterId)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
router := obj.(*SRouter)
|
||||
return router, nil
|
||||
}
|
||||
|
||||
func (man *SIfaceManager) removeByFilter(ctx context.Context, userCred mcclient.TokenCredential, filter map[string]string) error {
|
||||
ifaces, err := man.getByFilter(filter)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var errs []error
|
||||
for i := range ifaces {
|
||||
iface := &ifaces[i]
|
||||
if err := iface.remove(ctx, userCred); err != nil {
|
||||
errs = append(errs, err)
|
||||
}
|
||||
}
|
||||
return yerrors.NewAggregate(errs)
|
||||
}
|
||||
|
||||
func (man *SIfaceManager) removeByRouter(ctx context.Context, userCred mcclient.TokenCredential, router *SRouter) error {
|
||||
err := man.removeByFilter(ctx, userCred, map[string]string{
|
||||
"router_id": router.Id,
|
||||
})
|
||||
return err
|
||||
}
|
||||
|
||||
func (man *SIfaceManager) removeByMeshNetwork(ctx context.Context, userCred mcclient.TokenCredential, mn *SMeshNetwork) error {
|
||||
err := man.removeByFilter(ctx, userCred, map[string]string{
|
||||
"network_id": mn.Id,
|
||||
})
|
||||
return err
|
||||
}
|
||||
|
||||
func (man *SIfaceManager) removeByMeshNetworkRouter(ctx context.Context, userCred mcclient.TokenCredential, mn *SMeshNetwork, router *SRouter) error {
|
||||
err := man.removeByFilter(ctx, userCred, map[string]string{
|
||||
"network_id": mn.Id,
|
||||
"router_id": router.Id,
|
||||
})
|
||||
return err
|
||||
}
|
||||
func (man *SIfaceManager) getByRouter(router *SRouter) ([]SIface, error) {
|
||||
return man.getByFilter(map[string]string{
|
||||
"router_id": router.Id,
|
||||
})
|
||||
}
|
||||
|
||||
func (man *SIfaceManager) getByRouterIfname(router *SRouter, ifname string) (*SIface, error) {
|
||||
return man.getOneByFilter(map[string]string{
|
||||
"router_id": router.Id,
|
||||
"ifname": ifname,
|
||||
})
|
||||
}
|
||||
|
||||
func (man *SIfaceManager) getByFilter(filter map[string]string) ([]SIface, error) {
|
||||
ifaces := []SIface{}
|
||||
q := man.Query()
|
||||
for key, val := range filter {
|
||||
q = q.Equals(key, val)
|
||||
}
|
||||
if err := db.FetchModelObjects(IfaceManager, q, &ifaces); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return ifaces, nil
|
||||
}
|
||||
|
||||
func (man *SIfaceManager) getOneByFilter(filter map[string]string) (*SIface, error) {
|
||||
ifaces, err := man.getByFilter(filter)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(ifaces) == 0 {
|
||||
return nil, fmt.Errorf("cannot find iface for condition: %#v", filter)
|
||||
}
|
||||
if len(ifaces) > 1 {
|
||||
return nil, fmt.Errorf("found more than 1 ifaces for condition: %#v", filter)
|
||||
}
|
||||
return &ifaces[0], nil
|
||||
}
|
||||
|
||||
func (man *SIfaceManager) 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 *SIfaceManager) getByRouterNetwork(ctx context.Context, userCred mcclient.TokenCredential, router *SRouter, mn *SMeshNetwork) ([]SIface, error) {
|
||||
ifaces, err := man.getByFilter(map[string]string{
|
||||
"router_id": router.Id,
|
||||
"network_id": mn.Id,
|
||||
})
|
||||
return ifaces, err
|
||||
}
|
||||
|
||||
func (man *SIfaceManager) getOneByRouterNetwork(ctx context.Context, userCred mcclient.TokenCredential, router *SRouter, mn *SMeshNetwork) (*SIface, error) {
|
||||
iface, err := man.getOneByFilter(map[string]string{
|
||||
"router_id": router.Id,
|
||||
"network_id": mn.Id,
|
||||
})
|
||||
return iface, err
|
||||
}
|
||||
|
||||
func (man *SIfaceManager) getByMeshNetworkMember(member *SMeshNetworkMember) (*SIface, error) {
|
||||
iface, err := man.getOneByFilter(map[string]string{
|
||||
"router_id": member.RouterId,
|
||||
"network_id": member.MeshNetworkId,
|
||||
})
|
||||
return iface, err
|
||||
}
|
||||
|
||||
func (man *SIfaceManager) getNextName(filter map[string]string, base string) (string, error) {
|
||||
ifaces, err := man.getByFilter(filter)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
occupied := map[int]struct{}{}
|
||||
for i := range ifaces {
|
||||
iface := &ifaces[i]
|
||||
if strings.HasPrefix(iface.Ifname, base) {
|
||||
istr := iface.Ifname[len(base):]
|
||||
i, err := strconv.ParseUint(istr, 10, 16)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
occupied[int(i)] = struct{}{}
|
||||
}
|
||||
}
|
||||
for i := 0; i < 65536; i++ {
|
||||
if _, ok := occupied[i]; !ok {
|
||||
return fmt.Sprintf("%s%d", base, i), nil
|
||||
}
|
||||
}
|
||||
return "", fmt.Errorf("all names occupied")
|
||||
}
|
||||
|
||||
func (man *SIfaceManager) addWireguardIface(ctx context.Context, userCred mcclient.TokenCredential, router *SRouter, mn *SMeshNetwork) (*SIface, error) {
|
||||
k := cnutils.MustNewKey()
|
||||
port := router.mustFindFreePort(ctx)
|
||||
iface := &SIface{
|
||||
RouterId: router.Id,
|
||||
PrivateKey: k.String(),
|
||||
PublicKey: k.PublicKey().String(),
|
||||
ListenPort: port,
|
||||
}
|
||||
iface.IsSystem = true
|
||||
|
||||
{ // ifname
|
||||
if name, err := man.getNextName(map[string]string{
|
||||
"router_id": router.Id,
|
||||
}, "wg"); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
iface.Ifname = name
|
||||
}
|
||||
}
|
||||
|
||||
{ // obj name
|
||||
name := router.Name + "-"
|
||||
if mn != nil {
|
||||
iface.NetworkId = mn.Id
|
||||
name += mn.Name + "-"
|
||||
}
|
||||
name += fmt.Sprintf("%d", port)
|
||||
iface.Name = name
|
||||
}
|
||||
|
||||
iface.SetModelManager(man, iface)
|
||||
err := man.TableSpec().Insert(iface)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := RuleManager.addWireguardIfaceRules(ctx, userCred, iface); err != nil {
|
||||
iface.Delete(ctx, userCred)
|
||||
return nil, err
|
||||
}
|
||||
return iface, nil
|
||||
}
|
||||
|
||||
func (man *SIfaceManager) addIface(ctx context.Context, userCred mcclient.TokenCredential, router *SRouter, ifname string) (*SIface, error) {
|
||||
if err := man.checkExistenceByFilter(map[string]string{
|
||||
"router_id": router.Id,
|
||||
"ifname": ifname,
|
||||
}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
iface := &SIface{
|
||||
RouterId: router.Id,
|
||||
Ifname: ifname,
|
||||
}
|
||||
iface.SetModelManager(man, iface)
|
||||
err := man.TableSpec().Insert(iface)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return iface, nil
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
// 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
|
||||
|
||||
func InitDB() error {
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,152 @@
|
||||
// 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"
|
||||
"strings"
|
||||
|
||||
"yunion.io/x/log"
|
||||
yerrors "yunion.io/x/pkg/util/errors"
|
||||
"yunion.io/x/pkg/util/netutils"
|
||||
|
||||
"yunion.io/x/onecloud/pkg/cloudcommon/db"
|
||||
"yunion.io/x/onecloud/pkg/mcclient"
|
||||
)
|
||||
|
||||
type SMeshNetworkMember struct {
|
||||
db.SStandaloneResourceBase
|
||||
|
||||
MeshNetworkId string
|
||||
RouterId string
|
||||
AdvertiseSubnets string
|
||||
}
|
||||
|
||||
type SMeshNetworkMemberManager struct {
|
||||
db.SStandaloneResourceBaseManager
|
||||
}
|
||||
|
||||
var MeshNetworkMemberManager *SMeshNetworkMemberManager
|
||||
|
||||
func init() {
|
||||
MeshNetworkMemberManager = &SMeshNetworkMemberManager{
|
||||
SStandaloneResourceBaseManager: db.NewStandaloneResourceBaseManager(
|
||||
SMeshNetworkMember{},
|
||||
"meshnetwork_members_tbl",
|
||||
"meshnetwork_member",
|
||||
"meshnetwork_members",
|
||||
),
|
||||
}
|
||||
MeshNetworkMemberManager.SetVirtualObject(MeshNetworkMemberManager)
|
||||
}
|
||||
|
||||
func (member *SMeshNetworkMember) subnetsStrList() []string {
|
||||
return strings.Split(member.AdvertiseSubnets, ",")
|
||||
}
|
||||
|
||||
func (member *SMeshNetworkMember) subnetsParsed() Subnets {
|
||||
parts := member.subnetsStrList()
|
||||
r := make([]*netutils.IPV4Prefix, 0, len(parts))
|
||||
for _, part := range parts {
|
||||
p, err := netutils.NewIPV4Prefix(part)
|
||||
if err != nil {
|
||||
log.Errorf("%s: invalid subnet sneaked in: %s", member.Id, part)
|
||||
return nil
|
||||
}
|
||||
r = append(r, &p)
|
||||
}
|
||||
return Subnets(r)
|
||||
}
|
||||
|
||||
func (member *SMeshNetworkMember) getRouter() (*SRouter, error) {
|
||||
obj, err := db.FetchById(RouterManager, member.RouterId)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
router := obj.(*SRouter)
|
||||
return router, nil
|
||||
}
|
||||
|
||||
func (man *SMeshNetworkMemberManager) getByFilter(filter map[string]string) ([]SMeshNetworkMember, error) {
|
||||
members := []SMeshNetworkMember{}
|
||||
q := man.Query()
|
||||
for key, val := range filter {
|
||||
q = q.Equals(key, val)
|
||||
}
|
||||
if err := db.FetchModelObjects(man, q, &members); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return members, nil
|
||||
}
|
||||
|
||||
func (man *SMeshNetworkMemberManager) removeByFilter(ctx context.Context, userCred mcclient.TokenCredential, filter map[string]string) error {
|
||||
members, err := man.getByFilter(filter)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var errs []error
|
||||
for i := range members {
|
||||
if err := members[i].Delete(ctx, userCred); err != nil {
|
||||
errs = append(errs, err)
|
||||
}
|
||||
}
|
||||
return yerrors.NewAggregate(errs)
|
||||
}
|
||||
|
||||
func (man *SMeshNetworkMemberManager) removeByRouter(ctx context.Context, userCred mcclient.TokenCredential, router *SRouter) error {
|
||||
err := man.removeByFilter(ctx, userCred, map[string]string{
|
||||
"router_id": router.Id,
|
||||
})
|
||||
return err
|
||||
}
|
||||
|
||||
func (man *SMeshNetworkMemberManager) removeByMeshNetwork(ctx context.Context, userCred mcclient.TokenCredential, mn *SMeshNetwork) error {
|
||||
err := man.removeByFilter(ctx, userCred, map[string]string{
|
||||
"mesh_network_id": mn.Id,
|
||||
})
|
||||
return err
|
||||
}
|
||||
|
||||
func (man *SMeshNetworkMemberManager) removeByMeshNetworkRouter(ctx context.Context, userCred mcclient.TokenCredential,
|
||||
mn *SMeshNetwork, router *SRouter) error {
|
||||
err := man.removeByFilter(ctx, userCred, map[string]string{
|
||||
"mesh_network_id": mn.Id,
|
||||
"router_id": router.Id,
|
||||
})
|
||||
return err
|
||||
}
|
||||
|
||||
func (man *SMeshNetworkMemberManager) getMemebersByMeshNetwork(ctx context.Context, userCred mcclient.TokenCredential, mn *SMeshNetwork) ([]SMeshNetworkMember, error) {
|
||||
members := []SMeshNetworkMember{}
|
||||
q := man.Query().Equals("mesh_network_id", mn.Id)
|
||||
err := db.FetchModelObjects(man, q, &members)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return members, nil
|
||||
}
|
||||
|
||||
func (man *SMeshNetworkMemberManager) addMember(ctx context.Context, userCred mcclient.TokenCredential, mn *SMeshNetwork, router *SRouter, nets Subnets) (*SMeshNetworkMember, error) {
|
||||
member := &SMeshNetworkMember{
|
||||
MeshNetworkId: mn.Id,
|
||||
RouterId: router.Id,
|
||||
AdvertiseSubnets: nets.String(),
|
||||
}
|
||||
member.SetModelManager(man, member)
|
||||
member.Name = fmt.Sprintf("%s-%s", mn.Name, router.Name)
|
||||
man.TableSpec().Insert(member)
|
||||
return member, nil
|
||||
}
|
||||
@@ -0,0 +1,157 @@
|
||||
// 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"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
|
||||
"yunion.io/x/jsonutils"
|
||||
yerrors "yunion.io/x/pkg/util/errors"
|
||||
|
||||
"yunion.io/x/onecloud/pkg/cloudcommon/db"
|
||||
"yunion.io/x/onecloud/pkg/httperrors"
|
||||
"yunion.io/x/onecloud/pkg/mcclient"
|
||||
)
|
||||
|
||||
type SMeshNetwork struct {
|
||||
db.SStandaloneResourceBase
|
||||
}
|
||||
|
||||
type SMeshNetworkManager struct {
|
||||
db.SStandaloneResourceBaseManager
|
||||
}
|
||||
|
||||
var MeshNetworkManager *SMeshNetworkManager
|
||||
|
||||
func init() {
|
||||
MeshNetworkManager = &SMeshNetworkManager{
|
||||
SStandaloneResourceBaseManager: db.NewStandaloneResourceBaseManager(
|
||||
SMeshNetwork{},
|
||||
"meshnetworks_tbl",
|
||||
"meshnetwork",
|
||||
"meshnetworks",
|
||||
),
|
||||
}
|
||||
MeshNetworkManager.SetVirtualObject(MeshNetworkManager)
|
||||
}
|
||||
|
||||
func (mn *SMeshNetwork) CustomizeDelete(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) error {
|
||||
var errs []error
|
||||
if err := MeshNetworkMemberManager.removeByMeshNetwork(ctx, userCred, mn); err != nil {
|
||||
errs = append(errs, err)
|
||||
}
|
||||
if err := IfaceManager.removeByMeshNetwork(ctx, userCred, mn); err != nil {
|
||||
errs = append(errs, err)
|
||||
}
|
||||
if err := mn.SStandaloneResourceBase.CustomizeDelete(ctx, userCred, query, data); err != nil {
|
||||
errs = append(errs, err)
|
||||
}
|
||||
return yerrors.NewAggregate(errs)
|
||||
}
|
||||
|
||||
func (mn *SMeshNetwork) AllowPerformRealize(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) bool {
|
||||
return db.IsAdminAllowPerform(userCred, mn, "realize")
|
||||
}
|
||||
|
||||
func (mn *SMeshNetwork) PerformRealize(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) {
|
||||
members, err := MeshNetworkMemberManager.getMemebersByMeshNetwork(ctx, userCred, mn)
|
||||
if err != nil {
|
||||
return nil, httperrors.NewBadRequestError("fetch members: %v", err)
|
||||
}
|
||||
var errs []error
|
||||
for i := range members {
|
||||
member := &members[i]
|
||||
router, err := member.getRouter()
|
||||
if err != nil {
|
||||
errs = append(errs, errors.WithMessagef(err, "get router %s", member.RouterId))
|
||||
continue
|
||||
}
|
||||
if err := router.realize(ctx, userCred); err != nil {
|
||||
errs = append(errs, errors.WithMessagef(err, "realize router %s", router.Name))
|
||||
}
|
||||
}
|
||||
err = yerrors.NewAggregate(errs)
|
||||
if err != nil {
|
||||
err = httperrors.NewBadRequestError("some router realization failed: %s", err)
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
func (mn *SMeshNetwork) addRouter(ctx context.Context, userCred mcclient.TokenCredential, router *SRouter, nets Subnets) error {
|
||||
// XXX lock
|
||||
members, err := MeshNetworkMemberManager.getMemebersByMeshNetwork(ctx, userCred, mn)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for i := range members {
|
||||
member := &members[i]
|
||||
if member.RouterId == router.Id {
|
||||
return fmt.Errorf("router %s is already a member of %s",
|
||||
router.Name, mn.Name)
|
||||
}
|
||||
memberSubnets := member.subnetsParsed()
|
||||
if _, p := memberSubnets.ContainsAnyEx(nets); p != nil {
|
||||
return fmt.Errorf("router %s subnet %s already advertised by member %s(%s)",
|
||||
router.Name, p.String(), member.Name, member.Id)
|
||||
}
|
||||
}
|
||||
_, err = MeshNetworkMemberManager.addMember(ctx, userCred, mn, router, nets)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
newIface, err := IfaceManager.addWireguardIface(ctx, userCred, router, mn)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// XXX allocate an iface and populate iface peers
|
||||
var errs []error
|
||||
for i := range members {
|
||||
member := &members[i]
|
||||
memberIface, err := IfaceManager.getByMeshNetworkMember(member)
|
||||
if err != nil {
|
||||
errs = append(errs, err)
|
||||
continue
|
||||
}
|
||||
if err := memberIface.addOrUpdatePeer(ctx, userCred, newIface, nets, router); err != nil {
|
||||
errs = append(errs, err)
|
||||
}
|
||||
if memberHost, err := RouterManager.getById(member.RouterId); err != nil {
|
||||
errs = append(errs, err)
|
||||
} else {
|
||||
if err := newIface.addOrUpdatePeer(ctx, userCred, memberIface, member.subnetsParsed(), memberHost); err != nil {
|
||||
errs = append(errs, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
return yerrors.NewAggregate(errs)
|
||||
}
|
||||
|
||||
func (mn *SMeshNetwork) removeRouter(ctx context.Context, userCred mcclient.TokenCredential, router *SRouter) error {
|
||||
if err := MeshNetworkMemberManager.removeByMeshNetworkRouter(ctx, userCred, mn, router); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := IfaceManager.removeByMeshNetworkRouter(ctx, userCred, mn, router); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (man *SMeshNetworkManager) removeRouter(ctx context.Context, userCred mcclient.TokenCredential, router *SRouter) error {
|
||||
err := MeshNetworkMemberManager.removeByRouter(ctx, userCred, router)
|
||||
return err
|
||||
}
|
||||
@@ -0,0 +1,295 @@
|
||||
// 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/log"
|
||||
"yunion.io/x/pkg/gotypes"
|
||||
yerrors "yunion.io/x/pkg/util/errors"
|
||||
"yunion.io/x/pkg/util/netutils"
|
||||
|
||||
"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"
|
||||
)
|
||||
|
||||
// Add revision?
|
||||
type SRouter struct {
|
||||
db.SStandaloneResourceBase
|
||||
|
||||
User string `nullable:"false" list:"user" update:"user" create:"optional"`
|
||||
Host string `nullable:"false" list:"user" update:"user" create:"required"`
|
||||
Port int `nullable:"false" list:"user" update:"user" create:"optional"`
|
||||
PrivateKey string `nullable:"true" update:"user" create:"optional"` // do not allow get, list
|
||||
|
||||
RealizeWgIfaces bool `width:"16" charset:"ascii" nullable:"false" list:"user" create:"optional" update:"user"`
|
||||
RealizeRoutes bool `width:"16" charset:"ascii" nullable:"false" list:"user" create:"optional" update:"user"`
|
||||
RealizeRules bool `width:"16" charset:"ascii" nullable:"false" list:"user" create:"optional" update:"user"`
|
||||
}
|
||||
|
||||
type SRouterManager struct {
|
||||
db.SStandaloneResourceBaseManager
|
||||
}
|
||||
|
||||
var RouterManager *SRouterManager
|
||||
|
||||
func init() {
|
||||
RouterManager = &SRouterManager{
|
||||
SStandaloneResourceBaseManager: db.NewStandaloneResourceBaseManager(
|
||||
SRouter{},
|
||||
"routers_tbl",
|
||||
"router",
|
||||
"routers",
|
||||
),
|
||||
}
|
||||
RouterManager.SetVirtualObject(RouterManager)
|
||||
}
|
||||
|
||||
func (man *SRouterManager) ValidateCreateData(ctx context.Context, userCred mcclient.TokenCredential, ownerId mcclient.IIdentityProvider, query jsonutils.JSONObject, data *jsonutils.JSONDict) (*jsonutils.JSONDict, error) {
|
||||
if _, err := man.SStandaloneResourceBaseManager.ValidateCreateData(ctx, userCred, ownerId, query, data); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
vs := []validators.IValidator{
|
||||
validators.NewStringNonEmptyValidator("user").Default("cloudroot"),
|
||||
validators.NewStringNonEmptyValidator("host"),
|
||||
validators.NewPortValidator("port").Default(22),
|
||||
validators.NewSSHKeyValidator("private_key").Optional(true),
|
||||
validators.NewBoolValidator("realize_wg_ifaces").Default(true),
|
||||
validators.NewBoolValidator("realize_routes").Default(true),
|
||||
validators.NewBoolValidator("realize_rules").Default(true),
|
||||
}
|
||||
for _, v := range vs {
|
||||
if err := v.Validate(data); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
// populate ssh credential through "cloudhost"
|
||||
//
|
||||
// if ! skip validation {
|
||||
// ssh credential validation
|
||||
// }
|
||||
return data, nil
|
||||
}
|
||||
|
||||
func (router *SRouter) PostCreate(ctx context.Context, userCred mcclient.TokenCredential, ownerId mcclient.IIdentityProvider, query jsonutils.JSONObject, data jsonutils.JSONObject) {
|
||||
err := RuleManager.addRouterRules(ctx, userCred, router)
|
||||
if err != nil {
|
||||
log.Errorf("add router rule: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func (man *SRouterManager) getById(id string) (*SRouter, error) {
|
||||
m, err := db.FetchById(man, id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
router := m.(*SRouter)
|
||||
return router, err
|
||||
}
|
||||
|
||||
func (router *SRouter) ValidateUpdateData(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data *jsonutils.JSONDict) (*jsonutils.JSONDict, error) {
|
||||
if _, err := router.SStandaloneResourceBase.ValidateUpdateData(ctx, userCred, query, data); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
vs := []validators.IValidator{
|
||||
validators.NewStringNonEmptyValidator("user"),
|
||||
validators.NewStringNonEmptyValidator("host"),
|
||||
validators.NewPortValidator("port"),
|
||||
validators.NewSSHKeyValidator("private_key").Optional(true),
|
||||
validators.NewBoolValidator("realize_wg_ifaces"),
|
||||
validators.NewBoolValidator("realize_routes"),
|
||||
validators.NewBoolValidator("realize_rules"),
|
||||
}
|
||||
for _, v := range vs {
|
||||
v.Optional(true)
|
||||
if err := v.Validate(data); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
data.Set("_old_endpoint", jsonutils.NewString(router.endpointIP()))
|
||||
return data, nil
|
||||
}
|
||||
|
||||
func (router *SRouter) PostUpdate(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) {
|
||||
endpointOld, _ := data.GetString("_old_endpoint")
|
||||
endpoint := router.endpointIP()
|
||||
if endpoint != endpointOld {
|
||||
err := IfacePeerManager.updateEndpointIPByPeerRouter(ctx, endpoint, router)
|
||||
if err != nil {
|
||||
log.Errorf("updating peer endpoint failed: %v", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (router *SRouter) CustomizeDelete(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) error {
|
||||
var errs []error
|
||||
if err := MeshNetworkManager.removeRouter(ctx, userCred, router); err != nil {
|
||||
errs = append(errs, err)
|
||||
}
|
||||
if err := IfaceManager.removeByRouter(ctx, userCred, router); err != nil {
|
||||
errs = append(errs, err)
|
||||
}
|
||||
if err := RuleManager.removeByRouter(ctx, userCred, router); err != nil {
|
||||
errs = append(errs, err)
|
||||
}
|
||||
if err := router.SStandaloneResourceBase.CustomizeDelete(ctx, userCred, query, data); err != nil {
|
||||
errs = append(errs, err)
|
||||
}
|
||||
return yerrors.NewAggregate(errs)
|
||||
}
|
||||
|
||||
func (router *SRouter) AllowPerformJoinMeshNetwork(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) bool {
|
||||
return db.IsAdminAllowPerform(userCred, router, "join-mesh-network")
|
||||
}
|
||||
|
||||
func (router *SRouter) PerformJoinMeshNetwork(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) {
|
||||
mnV := validators.NewModelIdOrNameValidator("mesh_network", "meshnetwork", userCred)
|
||||
advSubnetsV := validators.NewValidatorByActor("advertise_subnets",
|
||||
validators.NewActorJoinedBy(",", validators.NewActorIPv4Prefix()))
|
||||
{
|
||||
vs := []validators.IValidator{
|
||||
mnV,
|
||||
advSubnetsV,
|
||||
}
|
||||
jd, ok := data.(*jsonutils.JSONDict)
|
||||
if !ok {
|
||||
return nil, httperrors.NewBadRequestError("expecting json dict")
|
||||
}
|
||||
for _, v := range vs {
|
||||
if err := v.Validate(jd); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
}
|
||||
// TODO dedup
|
||||
nets := gotypes.ConvertSliceElemType(advSubnetsV.Value, (**netutils.IPV4Prefix)(nil)).([]*netutils.IPV4Prefix)
|
||||
if len(nets) == 0 {
|
||||
return nil, httperrors.NewBadRequestError("advertise_subnets must not be empty")
|
||||
}
|
||||
mn := mnV.Model.(*SMeshNetwork)
|
||||
if err := mn.addRouter(ctx, userCred, router, nets); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return data, nil
|
||||
}
|
||||
|
||||
func (router *SRouter) AllowPerformLeaveMeshNetwork(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) bool {
|
||||
return db.IsAdminAllowPerform(userCred, router, "leave-mesh-network")
|
||||
}
|
||||
|
||||
func (router *SRouter) PerformLeaveMeshNetwork(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) {
|
||||
jd, ok := data.(*jsonutils.JSONDict)
|
||||
if !ok {
|
||||
return nil, httperrors.NewBadRequestError("expecting json dict")
|
||||
}
|
||||
mnV := validators.NewModelIdOrNameValidator("mesh_network", "meshnetwork", userCred)
|
||||
if err := mnV.Validate(jd); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
mn := mnV.Model.(*SMeshNetwork)
|
||||
if err := mn.removeRouter(ctx, userCred, router); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (router *SRouter) AllowPerformRegisterIfname(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) bool {
|
||||
return db.IsAdminAllowPerform(userCred, router, "register-ifname")
|
||||
}
|
||||
|
||||
func (router *SRouter) PerformRegisterIfname(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) {
|
||||
jd, ok := data.(*jsonutils.JSONDict)
|
||||
if !ok {
|
||||
return nil, httperrors.NewBadRequestError("expecting json dict")
|
||||
}
|
||||
ifnameV := validators.NewRegexpValidator("ifname", regexpIfname)
|
||||
if err := ifnameV.Validate(jd); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
_, err := IfaceManager.addIface(ctx, userCred, router, ifnameV.Value)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (router *SRouter) AllowPerformUnregisterIfname(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) bool {
|
||||
return db.IsAdminAllowPerform(userCred, router, "unregister-ifname")
|
||||
}
|
||||
|
||||
func (router *SRouter) PerformUnregisterIfname(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) {
|
||||
jd, ok := data.(*jsonutils.JSONDict)
|
||||
if !ok {
|
||||
return nil, httperrors.NewBadRequestError("expecting json dict")
|
||||
}
|
||||
ifname, err := jd.GetString("ifname")
|
||||
if err != nil {
|
||||
return nil, httperrors.NewBadRequestError("get request ifname field: %v", err)
|
||||
}
|
||||
iface, err := IfaceManager.getByRouterIfname(router, ifname)
|
||||
if err != nil {
|
||||
return nil, httperrors.NewBadRequestError("get iface: %s", err)
|
||||
}
|
||||
if iface.NetworkId != "" {
|
||||
// XXX can_unregister
|
||||
return nil, httperrors.NewBadRequestError("please use leave network to unregister")
|
||||
}
|
||||
if err := iface.remove(ctx, userCred); err != nil {
|
||||
return nil, httperrors.NewBadRequestError("remove iface: %v", err)
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (router *SRouter) mustFindFreePort(ctx context.Context) int {
|
||||
// loop through ifaces listen port
|
||||
ifaces, err := IfaceManager.getByRouter(router)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
for sport := 20000; sport < 65536; sport++ {
|
||||
notfound := true
|
||||
for i := range ifaces {
|
||||
if ifaces[i].ListenPort == sport {
|
||||
notfound = false
|
||||
break
|
||||
}
|
||||
}
|
||||
if notfound {
|
||||
return sport
|
||||
}
|
||||
}
|
||||
panic(fmt.Sprintf("cannot find free port for host %s(%s)", router.Name, router.Id))
|
||||
}
|
||||
|
||||
func (router *SRouter) endpointIP() string {
|
||||
return router.Host
|
||||
}
|
||||
|
||||
func (router *SRouter) AllowPerformDeploy(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) bool {
|
||||
return db.IsAdminAllowPerform(userCred, router, "deploy")
|
||||
}
|
||||
|
||||
func (router *SRouter) PerformDeploy(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) {
|
||||
if err := RouterDeploymentManager.requestDeployment(ctx, userCred, router); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
@@ -0,0 +1,367 @@
|
||||
// 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 (
|
||||
"encoding/json"
|
||||
"encoding/xml"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
|
||||
"yunion.io/x/onecloud/pkg/util/ansiblev2"
|
||||
)
|
||||
|
||||
func (router *SRouter) ansibleHost() (*ansiblev2.Host, error) {
|
||||
vars := map[string]interface{}{
|
||||
"ansible_user": router.User,
|
||||
"ansible_host": router.Host,
|
||||
}
|
||||
if router.User != "root" {
|
||||
vars["ansible_become"] = "yes"
|
||||
}
|
||||
if router.PrivateKey != "" {
|
||||
vars["ansible_ssh_private_key_file"] = ".id_rsa"
|
||||
}
|
||||
if router.RealizeWgIfaces {
|
||||
if err := router.inventoryWireguardVars(vars); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
h := ansiblev2.NewHost()
|
||||
h.Vars = vars
|
||||
return h, nil
|
||||
}
|
||||
|
||||
func (router *SRouter) playFiles() map[string]string {
|
||||
r := map[string]string{
|
||||
"wgX.conf.j2": wgX_conf_j2,
|
||||
}
|
||||
if router.PrivateKey != "" {
|
||||
r[".id_rsa"] = router.PrivateKey
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
func (router *SRouter) playFilesStr() string {
|
||||
files := router.playFiles()
|
||||
r, _ := json.Marshal(files)
|
||||
return string(r)
|
||||
}
|
||||
|
||||
func (router *SRouter) inventoryWireguardVars(vars map[string]interface{}) error {
|
||||
type (
|
||||
WgNetworks []string
|
||||
WgInterface map[string]interface{}
|
||||
WgPeer map[string]interface{}
|
||||
WgPeers map[string]WgPeer
|
||||
)
|
||||
ifaces, err := IfaceManager.getByRouter(router)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
wgnetworks := WgNetworks{}
|
||||
for i := range ifaces {
|
||||
iface := &ifaces[i]
|
||||
if iface.PrivateKey == "" {
|
||||
continue
|
||||
}
|
||||
ifacePeers, err := IfacePeerManager.getByIface(iface)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
wgpeers := WgPeers{}
|
||||
for j := range ifacePeers {
|
||||
ifacePeer := &ifacePeers[j]
|
||||
if ifacePeer.PublicKey == "" {
|
||||
continue
|
||||
}
|
||||
wgpeer := WgPeer{
|
||||
"public_key": ifacePeer.PublicKey,
|
||||
"allowed_ips": ifacePeer.AllowedIPs,
|
||||
"endpoint": ifacePeer.Endpoint,
|
||||
}
|
||||
if ifacePeer.PersistentKeepalive > 0 {
|
||||
wgpeer["persistent_keepalive"] = ifacePeer.PersistentKeepalive
|
||||
}
|
||||
wgpeers[ifacePeer.Name] = wgpeer
|
||||
}
|
||||
if len(wgpeers) == 0 {
|
||||
continue
|
||||
}
|
||||
vars["wireguard_"+iface.Ifname+"_interface"] = WgInterface{
|
||||
"private_key": iface.PrivateKey,
|
||||
"listen_port": iface.ListenPort,
|
||||
}
|
||||
vars["wireguard_"+iface.Ifname+"_peers"] = wgpeers
|
||||
wgnetworks = append(wgnetworks, iface.Ifname)
|
||||
}
|
||||
vars["wireguard_networks"] = wgnetworks
|
||||
return nil
|
||||
}
|
||||
|
||||
func (router *SRouter) playInstallWireguard() *ansiblev2.Play {
|
||||
play := ansiblev2.NewPlay(
|
||||
&ansiblev2.Task{
|
||||
Name: "Enable EPEL",
|
||||
ModuleName: "package",
|
||||
ModuleArgs: map[string]interface{}{
|
||||
"name": "epel-release",
|
||||
"state": "present",
|
||||
},
|
||||
},
|
||||
&ansiblev2.Task{
|
||||
Name: "Check existence of wireguard repo file",
|
||||
ModuleName: "stat",
|
||||
ModuleArgs: map[string]interface{}{
|
||||
"path": "/etc/yum.repos.d/_copr_jdoss-wireguard.repo",
|
||||
},
|
||||
Register: "wireguard_repo",
|
||||
},
|
||||
&ansiblev2.Task{
|
||||
Name: "Enable wireguard repo from copr",
|
||||
ModuleName: "get_url",
|
||||
ModuleArgs: map[string]interface{}{
|
||||
"dest": "/etc/yum.repos.d/_copr_jdoss-wireguard.repo",
|
||||
"url": "https://copr.fedorainfracloud.org/coprs/jdoss/wireguard/repo/epel-7/jdoss-wireguard-epel-7.repo",
|
||||
},
|
||||
When: "(not wireguard_repo.stat.exists) or (wireguard_repo.stat.size < 10)",
|
||||
},
|
||||
&ansiblev2.Task{
|
||||
Name: "Install wireguard packages",
|
||||
ModuleName: "package",
|
||||
ModuleArgs: map[string]interface{}{
|
||||
"name": "{{ item }}",
|
||||
"state": "present",
|
||||
},
|
||||
WithPlugin: "items",
|
||||
WithPluginVal: []string{"wireguard-dkms", "wireguard-tools"},
|
||||
},
|
||||
&ansiblev2.Task{
|
||||
Name: "Create /etc/wireguard",
|
||||
ModuleName: "file",
|
||||
ModuleArgs: map[string]interface{}{
|
||||
"path": "/etc/wireguard",
|
||||
"state": "directory",
|
||||
"owner": "root",
|
||||
"group": "root",
|
||||
},
|
||||
},
|
||||
)
|
||||
play.Hosts = "all"
|
||||
play.Name = "Install WireGuard"
|
||||
return play
|
||||
}
|
||||
|
||||
func (router *SRouter) playDeployWireguardNetworks() *ansiblev2.Play {
|
||||
play := ansiblev2.NewPlay(
|
||||
&ansiblev2.ShellTask{
|
||||
Name: "List existing managed wireguard networks",
|
||||
Script: `grep -rnl 'Ansible managed' /etc/wireguard/ | grep '\.conf$' | cut -d/ -f4 | cut -d. -f1`,
|
||||
Register: "oldconfs",
|
||||
IgnoreErrors: true,
|
||||
},
|
||||
&ansiblev2.Task{
|
||||
Name: "Backup stale wireguard confs",
|
||||
ModuleName: "copy",
|
||||
ModuleArgs: map[string]interface{}{
|
||||
"src": "/etc/wireguard/{{ item }}.conf",
|
||||
"dest": "/etc/wireguard/{{ item }}.conf.stale",
|
||||
"remote_src": "yes",
|
||||
},
|
||||
WithPlugin: "items",
|
||||
WithPluginVal: "{{ oldconfs.stdout_lines }}",
|
||||
When: "(not oldconfs.failed) and (item not in wireguard_networks)",
|
||||
},
|
||||
&ansiblev2.Task{
|
||||
Name: "Remove stale wireguard confs",
|
||||
ModuleName: "file",
|
||||
ModuleArgs: map[string]interface{}{
|
||||
"path": "/etc/wireguard/{{ item }}.conf",
|
||||
"state": "absent",
|
||||
},
|
||||
WithPlugin: "items",
|
||||
WithPluginVal: "{{ oldconfs.stdout_lines }}",
|
||||
When: "(not oldconfs.failed) and (item not in wireguard_networks)",
|
||||
},
|
||||
&ansiblev2.Task{
|
||||
Name: "Disable stale wireguard networks",
|
||||
ModuleName: "service",
|
||||
ModuleArgs: map[string]interface{}{
|
||||
"name": "wg-quick@{{ item }}",
|
||||
"state": "stopped",
|
||||
"enabled": "no",
|
||||
},
|
||||
WithPlugin: "items",
|
||||
WithPluginVal: "{{ oldconfs.stdout_lines }}",
|
||||
When: "(not oldconfs.failed) and (item not in wireguard_networks)",
|
||||
},
|
||||
&ansiblev2.Task{
|
||||
Name: "Configure wireguard conf",
|
||||
ModuleName: "template",
|
||||
ModuleArgs: map[string]interface{}{
|
||||
"src": "wgX.conf.j2", // wgX_conf_j2
|
||||
"dest": "/etc/wireguard/{{ item }}.conf",
|
||||
"mode": 0600,
|
||||
},
|
||||
WithPlugin: "items",
|
||||
WithPluginVal: "{{ wireguard_networks }}",
|
||||
Register: "configuration",
|
||||
},
|
||||
&ansiblev2.Task{
|
||||
Name: "Enable wg-quick@xx service",
|
||||
ModuleName: "service",
|
||||
ModuleArgs: map[string]interface{}{
|
||||
"name": "wg-quick@{{ item }}",
|
||||
"enabled": "yes",
|
||||
},
|
||||
WithPlugin: "items",
|
||||
WithPluginVal: "{{ wireguard_networks }}",
|
||||
},
|
||||
&ansiblev2.Task{
|
||||
Name: "Restart wg-quick@xx service",
|
||||
ModuleName: "service",
|
||||
ModuleArgs: map[string]interface{}{
|
||||
"name": "wg-quick@{{ item.1 }}",
|
||||
"state": "restarted",
|
||||
},
|
||||
WithPlugin: "indexed_items",
|
||||
WithPluginVal: "{{ wireguard_networks }}",
|
||||
When: "configuration.results[item.0].changed",
|
||||
},
|
||||
)
|
||||
play.Hosts = "all"
|
||||
play.Name = "Configure wireguard networks"
|
||||
return play
|
||||
}
|
||||
|
||||
func (router *SRouter) playDeployRoutes() (*ansiblev2.Play, error) {
|
||||
r, err := RouteManager.routeLinesRouter(router)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
tasks := []ansiblev2.ITask{}
|
||||
i := 0
|
||||
for ifname, lines := range r {
|
||||
if len(lines) == 0 {
|
||||
continue
|
||||
}
|
||||
iface, err := IfaceManager.getByRouterIfname(router, ifname)
|
||||
if err != nil {
|
||||
return nil, errors.WithMessagef(err, "get iface %s", ifname)
|
||||
}
|
||||
filename := "route-" + ifname
|
||||
content := strings.Join(lines, "\n") + "\n"
|
||||
registerVar := fmt.Sprintf("var%d", i)
|
||||
i += 1
|
||||
tasks = append(tasks, &ansiblev2.Task{
|
||||
Name: "Put routes for " + ifname,
|
||||
ModuleName: "copy",
|
||||
ModuleArgs: map[string]interface{}{
|
||||
"content": content,
|
||||
"dest": "/etc/sysconfig/network-scripts/" + filename,
|
||||
"owner": "root",
|
||||
"group": "root",
|
||||
"mode": "0644",
|
||||
},
|
||||
Register: registerVar,
|
||||
})
|
||||
if !iface.isTypeWireguard() {
|
||||
tasks = append(tasks, &ansiblev2.ShellTask{
|
||||
Name: fmt.Sprintf("Apply routes (Ifup/ifdown %s)", ifname),
|
||||
Script: fmt.Sprintf("ifdown %s; ifup %s", ifname, ifname),
|
||||
IgnoreErrors: true,
|
||||
When: fmt.Sprintf("%s.changed", registerVar),
|
||||
})
|
||||
}
|
||||
// apply by diff on changed
|
||||
}
|
||||
play := ansiblev2.NewPlay(tasks...)
|
||||
play.Hosts = "all"
|
||||
play.Name = "Configure routes"
|
||||
return play, nil
|
||||
}
|
||||
|
||||
func (router *SRouter) playDeployRules() (*ansiblev2.Play, error) {
|
||||
d, err := RuleManager.firewalldDirectByRouter(router)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
directXML, err := xml.MarshalIndent(d, "", " ")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
play := ansiblev2.NewPlay(
|
||||
&ansiblev2.Task{
|
||||
Name: "Install firewalld",
|
||||
ModuleName: "package",
|
||||
ModuleArgs: map[string]interface{}{
|
||||
"name": "firewalld",
|
||||
"state": "present",
|
||||
},
|
||||
},
|
||||
&ansiblev2.Task{
|
||||
Name: "Enable firewalld",
|
||||
ModuleName: "service",
|
||||
ModuleArgs: map[string]interface{}{
|
||||
"name": "firewalld",
|
||||
"enabled": "yes",
|
||||
},
|
||||
},
|
||||
&ansiblev2.Task{
|
||||
Name: "Put firewalld direct.xml",
|
||||
ModuleName: "copy",
|
||||
ModuleArgs: map[string]interface{}{
|
||||
"content": string(directXML),
|
||||
"dest": "/etc/firewalld/direct.xml",
|
||||
"owner": "root",
|
||||
"group": "root",
|
||||
"mode": "0644",
|
||||
},
|
||||
Register: "direct_xml",
|
||||
},
|
||||
&ansiblev2.Task{
|
||||
Name: "Restart firewalld",
|
||||
ModuleName: "service",
|
||||
ModuleArgs: map[string]interface{}{
|
||||
"name": "firewalld",
|
||||
"state": "restarted",
|
||||
},
|
||||
When: "direct_xml.changed",
|
||||
},
|
||||
)
|
||||
play.Hosts = "all"
|
||||
play.Name = "Configure firewall rules"
|
||||
return play, nil
|
||||
}
|
||||
|
||||
func (router *SRouter) playEssential() *ansiblev2.Play {
|
||||
play := ansiblev2.NewPlay(
|
||||
&ansiblev2.Task{
|
||||
Name: "Enable ip_forward",
|
||||
ModuleName: "sysctl",
|
||||
ModuleArgs: map[string]interface{}{
|
||||
"name": "net.ipv4.ip_forward",
|
||||
"value": "1",
|
||||
"state": "present",
|
||||
"reload": "yes",
|
||||
},
|
||||
},
|
||||
)
|
||||
play.Hosts = "all"
|
||||
play.Name = "Perform essential steps"
|
||||
return play
|
||||
}
|
||||
@@ -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 models
|
||||
|
||||
const wgX_conf_j2 = `
|
||||
{% set interface = lookup('vars', 'wireguard_' + item + '_interface') -%}
|
||||
{% set peers = lookup('vars', 'wireguard_' + item + '_peers') -%}
|
||||
|
||||
{% set interface_required_keys = { 'private_key': 'PrivateKey' } -%}
|
||||
{% set interface_optional_keys = {
|
||||
'address': 'Address',
|
||||
'listen_port': 'ListenPort',
|
||||
'fw_mark': 'FwMark',
|
||||
'dns': 'DNS',
|
||||
'mtu': 'MTU',
|
||||
'table': 'Table',
|
||||
'pre_up': 'PreUp',
|
||||
'post_up': 'PostUp',
|
||||
'pre_down': 'PreDown',
|
||||
'post_down': 'PostDown',
|
||||
'save_config': 'SaveConfig'
|
||||
} -%}
|
||||
{% set peer_required_keys = {
|
||||
'public_key': 'PublicKey',
|
||||
'allowed_ips': 'AllowedIPs'
|
||||
} -%}
|
||||
{% set peer_optional_keys = {
|
||||
'endpoint': 'EndPoint',
|
||||
'preshared_key': 'PresharedKey',
|
||||
'persistent_keepalive': 'PersistentKeepalive'
|
||||
} -%}
|
||||
{{ ansible_managed | comment }}
|
||||
|
||||
[Interface]
|
||||
{% for key, option in interface_required_keys.items() %}
|
||||
{{ option }} = {{ interface[key] }}
|
||||
{% endfor %}
|
||||
{% for key, option in interface_optional_keys.items() %}
|
||||
{% if interface[key] is defined %}
|
||||
{{ option }} = {{ interface[key] }}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
|
||||
{% for peer_name, peer in peers.items() %}
|
||||
[Peer] # {{ peer_name }}
|
||||
{% for key, option in peer_required_keys.items() %}
|
||||
{{ option }} = {{ peer[key] }}
|
||||
{% endfor %}
|
||||
{% for key, option in peer_optional_keys.items() %}
|
||||
{% if peer[key] is defined %}
|
||||
{{ option }} = {{ peer[key] }}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
|
||||
{% endfor %}
|
||||
`
|
||||
@@ -0,0 +1,57 @@
|
||||
// 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"
|
||||
|
||||
"yunion.io/x/onecloud/pkg/cloudcommon/db"
|
||||
"yunion.io/x/onecloud/pkg/mcclient"
|
||||
)
|
||||
|
||||
type SRouterDeployment struct {
|
||||
db.SStandaloneResourceBase
|
||||
|
||||
RouterId string
|
||||
AnsiblePlaybookId string
|
||||
|
||||
RouterRevision int
|
||||
}
|
||||
|
||||
type SRouterDeploymentManager struct {
|
||||
db.SStandaloneResourceBaseManager
|
||||
}
|
||||
|
||||
var RouterDeploymentManager *SRouterDeploymentManager
|
||||
|
||||
func init() {
|
||||
RouterDeploymentManager = &SRouterDeploymentManager{
|
||||
SStandaloneResourceBaseManager: db.NewStandaloneResourceBaseManager(
|
||||
SRouterDeployment{},
|
||||
"router_deployments_tbl",
|
||||
"router_deployment",
|
||||
"router_deployments",
|
||||
),
|
||||
}
|
||||
RouterDeploymentManager.SetVirtualObject(RouterDeploymentManager)
|
||||
}
|
||||
|
||||
func (man *SRouterDeploymentManager) requestDeployment(ctx context.Context, userCred mcclient.TokenCredential, router *SRouter) error {
|
||||
// XXX
|
||||
// make inventory
|
||||
// make playbook
|
||||
// queue a deploy ansible task
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
// 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"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
|
||||
"yunion.io/x/jsonutils"
|
||||
|
||||
"yunion.io/x/onecloud/pkg/cloudcommon/db"
|
||||
"yunion.io/x/onecloud/pkg/httperrors"
|
||||
"yunion.io/x/onecloud/pkg/mcclient"
|
||||
"yunion.io/x/onecloud/pkg/mcclient/auth"
|
||||
mcclient_modules "yunion.io/x/onecloud/pkg/mcclient/modules"
|
||||
"yunion.io/x/onecloud/pkg/util/ansiblev2"
|
||||
"yunion.io/x/onecloud/pkg/util/rand"
|
||||
)
|
||||
|
||||
func (router *SRouter) realize(ctx context.Context, userCred mcclient.TokenCredential) error {
|
||||
plays := []*ansiblev2.Play{
|
||||
router.playEssential(),
|
||||
}
|
||||
|
||||
host, err := router.ansibleHost()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if router.RealizeWgIfaces {
|
||||
plays = append(plays,
|
||||
router.playInstallWireguard(),
|
||||
router.playDeployWireguardNetworks(),
|
||||
)
|
||||
}
|
||||
|
||||
if router.RealizeRoutes {
|
||||
playRoutes, err := router.playDeployRoutes()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
plays = append(plays, playRoutes)
|
||||
}
|
||||
if router.RealizeRules {
|
||||
playRules, err := router.playDeployRules()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
plays = append(plays, playRules)
|
||||
}
|
||||
|
||||
inv := ansiblev2.NewInventory()
|
||||
inv.SetHost(router.Name, host)
|
||||
pb := ansiblev2.NewPlaybook(plays...)
|
||||
files := router.playFilesStr()
|
||||
|
||||
params := jsonutils.NewDict()
|
||||
params.Set("creator_mark", jsonutils.NewString("router:"+router.Id))
|
||||
params.Set("name", jsonutils.NewString(router.Name+"-"+fmt.Sprintf("%d-", router.UpdateVersion)+rand.String(5)))
|
||||
params.Set("inventory", jsonutils.NewString(inv.String()))
|
||||
params.Set("playbook", jsonutils.NewString(pb.String()))
|
||||
params.Set("files", jsonutils.NewString(files))
|
||||
cliSess := auth.GetSession(ctx, userCred, "", "")
|
||||
if _, err := mcclient_modules.AnsiblePlaybooksV2.Create(cliSess, params); err != nil {
|
||||
return errors.WithMessagef(err, "create ansible task")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (router *SRouter) AllowPerformRealize(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) bool {
|
||||
return db.IsAdminAllowPerform(userCred, router, "realize")
|
||||
}
|
||||
|
||||
func (router *SRouter) PerformRealize(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) {
|
||||
err := router.realize(ctx, userCred)
|
||||
if err != nil {
|
||||
return nil, httperrors.NewBadRequestError("%s", err)
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
@@ -0,0 +1,260 @@
|
||||
// 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"
|
||||
yerrors "yunion.io/x/pkg/util/errors"
|
||||
"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"
|
||||
"yunion.io/x/onecloud/pkg/util/rand"
|
||||
)
|
||||
|
||||
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) {
|
||||
if _, err := man.SStandaloneResourceBaseManager.ValidateCreateData(ctx, userCred, ownerId, query, data); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
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 nil, nil
|
||||
}
|
||||
|
||||
func (man *SRouteManager) ListItemFilter(ctx context.Context, q *sqlchemy.SQuery, userCred mcclient.TokenCredential, query jsonutils.JSONObject) (*sqlchemy.SQuery, error) {
|
||||
q, err := man.SStandaloneResourceBaseManager.ListItemFilter(ctx, q, userCred, query)
|
||||
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, data *jsonutils.JSONDict) (*jsonutils.JSONDict, error) {
|
||||
if _, err := route.SStandaloneResourceBase.ValidateUpdateData(ctx, userCred, query, data); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
vs := []validators.IValidator{
|
||||
validators.NewIPv4PrefixValidator("network"),
|
||||
validators.NewIPv4AddrValidator("gateway"),
|
||||
}
|
||||
for _, v := range vs {
|
||||
v.Optional(true)
|
||||
if err := v.Validate(data); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return nil, 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 yerrors.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
|
||||
}
|
||||
@@ -0,0 +1,459 @@
|
||||
// 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"
|
||||
"strings"
|
||||
|
||||
"yunion.io/x/jsonutils"
|
||||
yerrors "yunion.io/x/pkg/util/errors"
|
||||
"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"
|
||||
"yunion.io/x/onecloud/pkg/util/choices"
|
||||
"yunion.io/x/onecloud/pkg/util/firewalld"
|
||||
"yunion.io/x/onecloud/pkg/util/rand"
|
||||
)
|
||||
|
||||
type SRule struct {
|
||||
db.SStandaloneResourceBase
|
||||
|
||||
Prio int `nullable:"false" list:"user" update:"user" create:"optional"`
|
||||
|
||||
MatchSrcNet string `length:"32" nullable:"false" list:"user" update:"user" create:"optional"`
|
||||
MatchDestNet string `length:"32" nullable:"false" list:"user" update:"user" create:"optional"`
|
||||
MatchProto string `length:"8" nullable:"false" list:"user" update:"user" create:"optional"`
|
||||
MatchSrcPort int `nullable:"false" list:"user" update:"user" create:"optional"`
|
||||
MatchDestPort int `nullable:"false" list:"user" update:"user" create:"optional"`
|
||||
MatchInIfname string `length:"32" nullable:"false" list:"user" update:"user" create:"optional"`
|
||||
MatchOutIfname string `length:"32" nullable:"false" list:"user" update:"user" create:"optional"`
|
||||
|
||||
Action string `length:"32" nullable:"false" list:"user" update:"user" create:"required"`
|
||||
ActionOptions string `length:"32" nullable:"false" list:"user" update:"user" create:"optional"`
|
||||
|
||||
RouterId string `length:"32" nullable:"false" list:"user" create:"optional"`
|
||||
|
||||
IsSystem bool `nullable:"false" list:"user" create:"optional"`
|
||||
}
|
||||
|
||||
const (
|
||||
MIN_PRIO = 0
|
||||
MAX_PRIO = 2000
|
||||
DEF_PRIO = 0
|
||||
DEF_PRIO_ROUTER_FORWARD = 1000
|
||||
DEF_PRIO_MASQUERADE = 1000
|
||||
|
||||
ACT_SNAT = "SNAT"
|
||||
ACT_DNAT = "DNAT"
|
||||
ACT_MASQUERADE = "MASQUERADE"
|
||||
ACT_TCPMSS = "TCPMSS" // FORWARD chain for now
|
||||
ACT_INPUT_ACCEPT = "INPUT_ACCEPT"
|
||||
ACT_FORWARD_ACCEPT = "FORWARD_ACCEPT"
|
||||
|
||||
PROTO_TCP = "tcp"
|
||||
PROTO_UDP = "udp"
|
||||
)
|
||||
|
||||
var (
|
||||
actionChoices = choices.NewChoices(
|
||||
ACT_SNAT,
|
||||
ACT_DNAT,
|
||||
ACT_MASQUERADE,
|
||||
ACT_TCPMSS,
|
||||
|
||||
//"DROP",
|
||||
ACT_INPUT_ACCEPT,
|
||||
ACT_FORWARD_ACCEPT,
|
||||
//"REJECT",
|
||||
)
|
||||
protoChoices = choices.NewChoices(
|
||||
PROTO_TCP,
|
||||
PROTO_UDP,
|
||||
)
|
||||
)
|
||||
|
||||
type SRuleManager struct {
|
||||
db.SStandaloneResourceBaseManager
|
||||
}
|
||||
|
||||
var RuleManager *SRuleManager
|
||||
|
||||
func init() {
|
||||
RuleManager = &SRuleManager{
|
||||
SStandaloneResourceBaseManager: db.NewStandaloneResourceBaseManager(
|
||||
SRule{},
|
||||
"rules_tbl",
|
||||
"rule",
|
||||
"rules",
|
||||
),
|
||||
}
|
||||
RuleManager.SetVirtualObject(RuleManager)
|
||||
}
|
||||
|
||||
func (man *SRuleManager) validateData(ctx context.Context, userCred mcclient.TokenCredential, ownerId mcclient.IIdentityProvider, query jsonutils.JSONObject, data *jsonutils.JSONDict, rule *SRule) error {
|
||||
isUpdate := rule != nil
|
||||
routerV := validators.NewModelIdOrNameValidator("router", "router", ownerId)
|
||||
inIfnameV := validators.NewRegexpValidator("match_in_ifname", regexpIfname)
|
||||
outIfnameV := validators.NewRegexpValidator("match_out_ifname", regexpIfname)
|
||||
protoV := validators.NewStringChoicesValidator("match_proto", protoChoices)
|
||||
srcPortV := validators.NewPortValidator("match_src_port")
|
||||
destPortV := validators.NewPortValidator("match_dest_port")
|
||||
actionV := validators.NewStringChoicesValidator("action", protoChoices)
|
||||
actionOptsV := validators.NewStringLenRangeValidator("action_options", 0, 256)
|
||||
if isUpdate {
|
||||
inIfnameV.Default(rule.MatchInIfname)
|
||||
outIfnameV.Default(rule.MatchOutIfname)
|
||||
protoV.Default(rule.MatchProto)
|
||||
srcPortV.Default(int64(rule.MatchSrcPort))
|
||||
destPortV.Default(int64(rule.MatchDestPort))
|
||||
actionV.Default(rule.Action)
|
||||
actionOptsV.Default(rule.ActionOptions)
|
||||
}
|
||||
vs := []validators.IValidator{
|
||||
inIfnameV.Optional(true),
|
||||
outIfnameV.Optional(true),
|
||||
validators.NewIPv4PrefixValidator("match_src_net").Optional(true),
|
||||
validators.NewIPv4PrefixValidator("match_dest_net").Optional(true),
|
||||
protoV.Optional(true),
|
||||
srcPortV.Optional(true),
|
||||
destPortV.Optional(true),
|
||||
actionV,
|
||||
actionOptsV.Optional(true),
|
||||
routerV,
|
||||
}
|
||||
for _, v := range vs {
|
||||
if isUpdate {
|
||||
v.Optional(true)
|
||||
}
|
||||
if err := v.Validate(data); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if actionV.Value == ACT_TCPMSS {
|
||||
if protoV.Value != "" && protoV.Value != PROTO_TCP {
|
||||
return httperrors.NewBadRequestError("TCPMSS only works for proto tcp")
|
||||
}
|
||||
if protoV.Value == "" {
|
||||
data.Set("match_proto", jsonutils.NewString(PROTO_TCP))
|
||||
}
|
||||
} else if actionV.Value == ACT_DNAT {
|
||||
if outIfnameV.Value != "" {
|
||||
return httperrors.NewBadRequestError("cannot match out interface for DNAT")
|
||||
}
|
||||
} else if actionV.Value == ACT_SNAT {
|
||||
if inIfnameV.Value != "" {
|
||||
return httperrors.NewBadRequestError("cannot match in interface for SNAT")
|
||||
}
|
||||
}
|
||||
if (srcPortV.Value > 0 || destPortV.Value > 0) && protoV.Value == "" {
|
||||
return httperrors.NewBadRequestError("protocol must be specified when matching port")
|
||||
}
|
||||
|
||||
{
|
||||
prioDefault := int64(0)
|
||||
if !isUpdate && actionV.Value == ACT_MASQUERADE && !data.Contains("prio") {
|
||||
prioDefault = DEF_PRIO_MASQUERADE
|
||||
}
|
||||
prioV := validators.NewRangeValidator("prio", MIN_PRIO, MAX_PRIO)
|
||||
if !isUpdate {
|
||||
prioV.Default(prioDefault)
|
||||
}
|
||||
if err := prioV.Validate(data); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// XXX validate interface against db
|
||||
// XXX validate action options
|
||||
|
||||
if !isUpdate && !data.Contains("name") {
|
||||
router := routerV.Model.(*SRouter)
|
||||
data.Set("name", jsonutils.NewString(
|
||||
router.Name+"-"+rand.String(4),
|
||||
))
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (man *SRuleManager) ValidateCreateData(ctx context.Context, userCred mcclient.TokenCredential, ownerId mcclient.IIdentityProvider, query jsonutils.JSONObject, data *jsonutils.JSONDict) (*jsonutils.JSONDict, error) {
|
||||
if _, err := man.SStandaloneResourceBaseManager.ValidateCreateData(ctx, userCred, ownerId, query, data); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := man.validateData(ctx, userCred, ownerId, query, data, nil); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (man *SRuleManager) ListItemFilter(ctx context.Context, q *sqlchemy.SQuery, userCred mcclient.TokenCredential, query jsonutils.JSONObject) (*sqlchemy.SQuery, error) {
|
||||
q, err := man.SStandaloneResourceBaseManager.ListItemFilter(ctx, q, userCred, query)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
data := query.(*jsonutils.JSONDict)
|
||||
q, err = validators.ApplyModelFilters(q, data, []*validators.ModelFilterOptions{
|
||||
{Key: "router", ModelKeyword: "router", OwnerId: userCred},
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return q, nil
|
||||
}
|
||||
|
||||
func (rule *SRule) ValidateUpdateData(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data *jsonutils.JSONDict) (*jsonutils.JSONDict, error) {
|
||||
if _, err := rule.SStandaloneResourceBase.ValidateUpdateData(ctx, userCred, query, data); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := RuleManager.validateData(ctx, userCred, rule.GetOwnerId(), query, data, rule); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (man *SRuleManager) removeByRouter(ctx context.Context, userCred mcclient.TokenCredential, router *SRouter) error {
|
||||
rules, err := man.getByFilter(map[string]string{
|
||||
"router_id": router.Id,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var errs []error
|
||||
for j := range rules {
|
||||
if err := rules[j].Delete(ctx, userCred); err != nil {
|
||||
errs = append(errs, err)
|
||||
}
|
||||
}
|
||||
return yerrors.NewAggregate(errs)
|
||||
}
|
||||
|
||||
func (man *SRuleManager) getByFilter(filter map[string]string) ([]SRule, error) {
|
||||
rules := []SRule{}
|
||||
q := man.Query()
|
||||
for key, val := range filter {
|
||||
q = q.Equals(key, val)
|
||||
}
|
||||
if err := db.FetchModelObjects(RuleManager, q, &rules); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return rules, nil
|
||||
}
|
||||
|
||||
func (man *SRuleManager) getOneByFilter(filter map[string]string) (*SRule, error) {
|
||||
rules, err := man.getByFilter(filter)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(rules) == 0 {
|
||||
return nil, errNotFound(fmt.Errorf("cannot find rule: %#v", filter))
|
||||
}
|
||||
if len(rules) > 1 {
|
||||
return nil, errMoreThanOne(fmt.Errorf("found more than 1 rules: %#v", filter))
|
||||
}
|
||||
return &rules[0], nil
|
||||
}
|
||||
|
||||
func (man *SRuleManager) checkExistenceByFilter(filter map[string]string) error {
|
||||
rules, err := man.getByFilter(filter)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(rules) > 0 {
|
||||
return fmt.Errorf("rule exist: %s(%s)", rules[0].Name, rules[0].Id)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (man *SRuleManager) getByRouter(router *SRouter) ([]SRule, error) {
|
||||
filter := map[string]string{
|
||||
"router_id": router.Id,
|
||||
}
|
||||
rules, err := man.getByFilter(filter)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return rules, nil
|
||||
}
|
||||
|
||||
func (man *SRuleManager) addRouterRules(ctx context.Context, userCred mcclient.TokenCredential, router *SRouter) error {
|
||||
r := &SRule{
|
||||
Prio: DEF_PRIO_ROUTER_FORWARD,
|
||||
RouterId: router.Id,
|
||||
Action: ACT_FORWARD_ACCEPT,
|
||||
}
|
||||
r.IsSystem = true
|
||||
r.Name = router.Name + "-allow-forward-" + rand.String(4)
|
||||
r.SetModelManager(man, r)
|
||||
|
||||
err := man.addRule(ctx, userCred, r)
|
||||
return err
|
||||
}
|
||||
|
||||
func (man *SRuleManager) addWireguardIfaceRules(ctx context.Context, userCred mcclient.TokenCredential, iface *SIface) error {
|
||||
r := &SRule{
|
||||
RouterId: iface.RouterId,
|
||||
MatchProto: PROTO_UDP,
|
||||
MatchDestPort: iface.ListenPort,
|
||||
Action: ACT_INPUT_ACCEPT,
|
||||
}
|
||||
r.IsSystem = true
|
||||
r.Name = iface.Name + "-allow-" + fmt.Sprintf("%d-", iface.ListenPort) + rand.String(4)
|
||||
r.SetModelManager(man, r)
|
||||
|
||||
rules := man.ifaceTCPMSSRules(ctx, userCred, iface)
|
||||
rules = append(rules, r)
|
||||
err := man.addRules(ctx, userCred, rules)
|
||||
return err
|
||||
}
|
||||
|
||||
func (man *SRuleManager) ifaceTCPMSSRules(ctx context.Context, userCred mcclient.TokenCredential, iface *SIface) []*SRule {
|
||||
rules := []*SRule{
|
||||
&SRule{
|
||||
RouterId: iface.RouterId,
|
||||
MatchInIfname: iface.Ifname,
|
||||
MatchProto: PROTO_TCP,
|
||||
Action: ACT_TCPMSS,
|
||||
ActionOptions: "--clamp-mss-to-pmtu",
|
||||
},
|
||||
&SRule{
|
||||
RouterId: iface.RouterId,
|
||||
MatchProto: PROTO_TCP,
|
||||
MatchOutIfname: iface.Ifname,
|
||||
Action: ACT_TCPMSS,
|
||||
ActionOptions: "--clamp-mss-to-pmtu",
|
||||
},
|
||||
}
|
||||
for _, r := range rules {
|
||||
r.IsSystem = true
|
||||
r.Name = iface.Name + "-tcpmss-" + rand.String(4)
|
||||
r.SetModelManager(man, r)
|
||||
}
|
||||
return rules
|
||||
}
|
||||
|
||||
func (man *SRuleManager) addRules(ctx context.Context, userCred mcclient.TokenCredential, rules []*SRule) error {
|
||||
errs := []error{}
|
||||
for _, r := range rules {
|
||||
err := man.addRule(ctx, userCred, r)
|
||||
if err != nil {
|
||||
errs = append(errs, err)
|
||||
continue
|
||||
}
|
||||
}
|
||||
return yerrors.NewAggregate(errs)
|
||||
}
|
||||
|
||||
func (man *SRuleManager) addRule(ctx context.Context, userCred mcclient.TokenCredential, rule *SRule) error {
|
||||
return man.TableSpec().Insert(rule)
|
||||
}
|
||||
|
||||
func (rule *SRule) firewalldRule() (*firewalld.Rule, error) {
|
||||
var (
|
||||
prio int
|
||||
table string
|
||||
chain string
|
||||
action string
|
||||
body string
|
||||
matchOthers []string
|
||||
actionOthers []string
|
||||
)
|
||||
prio = rule.Prio
|
||||
switch rule.Action {
|
||||
case ACT_SNAT, ACT_DNAT, ACT_MASQUERADE:
|
||||
table = "nat"
|
||||
if rule.Action == ACT_DNAT {
|
||||
chain = "PREROUTING"
|
||||
} else {
|
||||
chain = "POSTROUTING"
|
||||
}
|
||||
action = rule.Action
|
||||
case ACT_TCPMSS:
|
||||
table = "mangle"
|
||||
chain = "FORWARD" // save INPUT, OUTPUT for future occasions
|
||||
action = rule.Action
|
||||
matchOthers = []string{"-m", "tcp", "--tcp-flags", "SYN,RST", "SYN"}
|
||||
if rule.ActionOptions == "" {
|
||||
actionOthers = []string{"--clamp-mss-to-pmtu"}
|
||||
}
|
||||
case ACT_INPUT_ACCEPT:
|
||||
table = "filter"
|
||||
chain = "INPUT"
|
||||
action = "ACCEPT"
|
||||
case ACT_FORWARD_ACCEPT:
|
||||
table = "filter"
|
||||
chain = "FORWARD"
|
||||
action = "ACCEPT"
|
||||
default:
|
||||
return nil, fmt.Errorf("unknown rule action: %s", rule.Action)
|
||||
}
|
||||
|
||||
{
|
||||
elms := []string{}
|
||||
if rule.MatchInIfname != "" {
|
||||
elms = append(elms, "-i", rule.MatchInIfname)
|
||||
}
|
||||
if rule.MatchOutIfname != "" {
|
||||
elms = append(elms, "-o", rule.MatchOutIfname)
|
||||
}
|
||||
if rule.MatchSrcNet != "" {
|
||||
elms = append(elms, "-s", rule.MatchSrcNet)
|
||||
}
|
||||
if rule.MatchDestNet != "" {
|
||||
elms = append(elms, "-d", rule.MatchDestNet)
|
||||
}
|
||||
if rule.MatchProto != "" {
|
||||
elms = append(elms, "-p", rule.MatchProto)
|
||||
}
|
||||
if rule.MatchSrcPort > 0 {
|
||||
elms = append(elms, "--sport", fmt.Sprintf("%d", rule.MatchSrcPort))
|
||||
}
|
||||
if rule.MatchDestPort > 0 {
|
||||
elms = append(elms, "--dport", fmt.Sprintf("%d", rule.MatchDestPort))
|
||||
}
|
||||
elms = append(elms, matchOthers...) // XXX empty elm
|
||||
elms = append(elms, "-j", action)
|
||||
if rule.ActionOptions != "" {
|
||||
elms = append(elms, rule.ActionOptions)
|
||||
}
|
||||
elms = append(elms, actionOthers...)
|
||||
body = strings.Join(elms, " ")
|
||||
}
|
||||
r := firewalld.NewIP4Rule(prio, table, chain, body)
|
||||
return r, nil
|
||||
}
|
||||
|
||||
func (man *SRuleManager) firewalldDirectByRouter(router *SRouter) (*firewalld.Direct, error) {
|
||||
rules, err := man.getByRouter(router)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
rs := []*firewalld.Rule{}
|
||||
errs := []error{}
|
||||
for i := range rules {
|
||||
rule := &rules[i]
|
||||
r, err := rule.firewalldRule()
|
||||
if err != nil {
|
||||
errs = append(errs, err)
|
||||
continue
|
||||
}
|
||||
rs = append(rs, r)
|
||||
}
|
||||
return firewalld.NewDirect(rs...), yerrors.NewAggregate(errs)
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package models
|
||||
|
||||
type (
|
||||
errNotFound error
|
||||
errMoreThanOne error
|
||||
)
|
||||
|
||||
func IsNotFound(err error) bool {
|
||||
_, ok := err.(errNotFound)
|
||||
return ok
|
||||
}
|
||||
|
||||
func IsMoreThanOne(err error) bool {
|
||||
_, ok := err.(errMoreThanOne)
|
||||
return ok
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"yunion.io/x/pkg/util/netutils"
|
||||
)
|
||||
|
||||
type Subnets []*netutils.IPV4Prefix
|
||||
|
||||
func (nets Subnets) StrList() []string {
|
||||
r := make([]string, 0, len(nets))
|
||||
for _, p := range nets {
|
||||
r = append(r, p.String())
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
func (nets Subnets) String() string {
|
||||
r := nets.StrList()
|
||||
return strings.Join(r, ",")
|
||||
}
|
||||
|
||||
func (nets Subnets) ContainsAny(nets1 Subnets) bool {
|
||||
contains, _ := nets.ContainsAnyEx(nets1)
|
||||
return contains
|
||||
}
|
||||
|
||||
func (nets Subnets) ContainsAnyEx(nets1 Subnets) (bool, *netutils.IPV4Prefix) {
|
||||
for _, p0 := range nets {
|
||||
for _, p1 := range nets1 {
|
||||
if p0.Equals(p1) {
|
||||
return true, p0
|
||||
}
|
||||
}
|
||||
}
|
||||
return false, nil
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
// Copyright 2019 Yunion
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package options // import "yunion.io/x/onecloud/pkg/cloudnet/options"
|
||||
@@ -0,0 +1,26 @@
|
||||
// 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 common_options "yunion.io/x/onecloud/pkg/cloudcommon/options"
|
||||
|
||||
type CloudnetOptions struct {
|
||||
common_options.CommonOptions
|
||||
common_options.DBOptions
|
||||
}
|
||||
|
||||
var (
|
||||
Options CloudnetOptions
|
||||
)
|
||||
@@ -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 service // import "yunion.io/x/onecloud/pkg/cloudnet/service"
|
||||
@@ -0,0 +1,57 @@
|
||||
// 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 service
|
||||
|
||||
// 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.
|
||||
|
||||
import (
|
||||
"yunion.io/x/onecloud/pkg/appsrv"
|
||||
"yunion.io/x/onecloud/pkg/appsrv/dispatcher"
|
||||
"yunion.io/x/onecloud/pkg/cloudcommon/db"
|
||||
"yunion.io/x/onecloud/pkg/cloudnet/models"
|
||||
)
|
||||
|
||||
func InitHandlers(app *appsrv.Application) {
|
||||
db.InitAllManagers()
|
||||
|
||||
db.RegisterModelManager(db.OpsLog)
|
||||
db.RegisterModelManager(db.TenantCacheManager)
|
||||
db.RegisterModelManager(db.UserCacheManager)
|
||||
db.RegisterModelManager(models.IfaceManager)
|
||||
db.RegisterModelManager(models.IfacePeerManager)
|
||||
db.RegisterModelManager(models.MeshNetworkMemberManager)
|
||||
for _, manager := range []db.IModelManager{
|
||||
models.RouterManager,
|
||||
models.MeshNetworkManager,
|
||||
models.RouteManager,
|
||||
models.RuleManager,
|
||||
} {
|
||||
db.RegisterModelManager(manager)
|
||||
handler := db.NewModelHandler(manager)
|
||||
dispatcher.AddModelDispatcher("", app, handler)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
// 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 service
|
||||
|
||||
import (
|
||||
"os"
|
||||
|
||||
_ "github.com/go-sql-driver/mysql"
|
||||
|
||||
"yunion.io/x/log"
|
||||
|
||||
"yunion.io/x/onecloud/pkg/cloudcommon"
|
||||
common_app "yunion.io/x/onecloud/pkg/cloudcommon/app"
|
||||
"yunion.io/x/onecloud/pkg/cloudcommon/db"
|
||||
common_options "yunion.io/x/onecloud/pkg/cloudcommon/options"
|
||||
"yunion.io/x/onecloud/pkg/cloudnet/models"
|
||||
"yunion.io/x/onecloud/pkg/cloudnet/options"
|
||||
)
|
||||
|
||||
func StartService() {
|
||||
opts := &options.Options
|
||||
common_options.ParseOptions(opts, os.Args, "cloudnet.conf", "cloudnet")
|
||||
|
||||
commonOpts := &opts.CommonOptions
|
||||
common_app.InitAuth(commonOpts, func() {
|
||||
log.Infof("Auth complete")
|
||||
})
|
||||
|
||||
dbOpts := &opts.DBOptions
|
||||
baseOpts := &opts.BaseOptions
|
||||
|
||||
app := common_app.InitApp(baseOpts, false)
|
||||
InitHandlers(app)
|
||||
|
||||
db.EnsureAppInitSyncDB(app, dbOpts, models.InitDB)
|
||||
defer cloudcommon.CloseDB()
|
||||
|
||||
common_app.ServeForever(app, baseOpts)
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
package utils // import "yunion.io/x/onecloud/pkg/cloudnet/utils"
|
||||
@@ -0,0 +1,28 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
|
||||
)
|
||||
|
||||
func MustNewKey() wgtypes.Key {
|
||||
key, err := wgtypes.GeneratePrivateKey()
|
||||
if err != nil {
|
||||
panic(fmt.Errorf("new key: %v", err))
|
||||
}
|
||||
return key
|
||||
}
|
||||
|
||||
func MustNewKeyString() string {
|
||||
key := MustNewKey()
|
||||
return key.String()
|
||||
}
|
||||
|
||||
func MustParseKeyString(k string) wgtypes.Key {
|
||||
key, err := wgtypes.ParseKey(k)
|
||||
if err != nil {
|
||||
panic(fmt.Errorf("invalid key: %v", err))
|
||||
}
|
||||
return key
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
|
||||
)
|
||||
|
||||
func TestKey(t *testing.T) {
|
||||
t.Run("parse", func(t *testing.T) {
|
||||
keyStr := "WJYVsrTtAae1QS9YzefV4OmVM6mkJglR+GEgxQpTs2g="
|
||||
pubkeyStr := "mOX0S5AuRqd8lQZWcqTlzOS+veo404gE7NyV4u3xVkg="
|
||||
pubkeyX, err := wgtypes.ParseKey(pubkeyStr)
|
||||
if err != nil {
|
||||
t.Fatalf("parse public key failed: %v", err)
|
||||
}
|
||||
|
||||
key, err := wgtypes.ParseKey(keyStr)
|
||||
if err != nil {
|
||||
t.Fatalf("parse private key failed: %v", err)
|
||||
}
|
||||
pubkey := key.PublicKey()
|
||||
if pubkey != pubkeyX {
|
||||
t.Errorf("derived public key does not match expected.\ngot %#v\nwant %#v", pubkey, pubkeyX)
|
||||
}
|
||||
})
|
||||
t.Run("generate", func(t *testing.T) {
|
||||
key, err := wgtypes.GeneratePrivateKey()
|
||||
if err != nil {
|
||||
t.Fatalf("generate private key failed: %v", err)
|
||||
}
|
||||
pubkey := key.PublicKey()
|
||||
t.Logf("private key: %s", key)
|
||||
t.Logf(" public key: %s", pubkey)
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
// 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 cloudnet
|
||||
|
||||
import (
|
||||
"yunion.io/x/onecloud/pkg/mcclient/modulebase"
|
||||
)
|
||||
|
||||
func NewCloudnetManager(keyword, keywordPlural string, columns, adminColumns []string) modulebase.ResourceManager {
|
||||
return modulebase.ResourceManager{
|
||||
BaseManager: *modulebase.NewBaseManager("cloudnet", "", "", columns, adminColumns),
|
||||
Keyword: keyword, KeywordPlural: keywordPlural}
|
||||
}
|
||||
|
||||
var (
|
||||
registerV2 = func(mod modulebase.BaseManagerInterface) {
|
||||
modulebase.Register("v2", mod)
|
||||
}
|
||||
)
|
||||
@@ -0,0 +1 @@
|
||||
package cloudnet // import "yunion.io/x/onecloud/pkg/mcclient/modules/cloudnet"
|
||||
@@ -0,0 +1,42 @@
|
||||
// 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 cloudnet
|
||||
|
||||
import (
|
||||
"yunion.io/x/onecloud/pkg/mcclient/modulebase"
|
||||
)
|
||||
|
||||
type MeshNetworkManager struct {
|
||||
modulebase.ResourceManager
|
||||
}
|
||||
|
||||
var (
|
||||
MeshNetworks MeshNetworkManager
|
||||
)
|
||||
|
||||
func init() {
|
||||
MeshNetworks = MeshNetworkManager{
|
||||
NewCloudnetManager(
|
||||
"meshnetwork",
|
||||
"meshnetworks",
|
||||
[]string{
|
||||
"id",
|
||||
"name",
|
||||
},
|
||||
[]string{"tenant"},
|
||||
),
|
||||
}
|
||||
registerV2(&MeshNetworks)
|
||||
}
|
||||
@@ -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 cloudnet
|
||||
|
||||
import (
|
||||
"yunion.io/x/onecloud/pkg/mcclient/modulebase"
|
||||
)
|
||||
|
||||
type RouterManager struct {
|
||||
modulebase.ResourceManager
|
||||
}
|
||||
|
||||
var (
|
||||
Routers RouterManager
|
||||
)
|
||||
|
||||
func init() {
|
||||
Routers = RouterManager{
|
||||
NewCloudnetManager(
|
||||
"router",
|
||||
"routers",
|
||||
[]string{
|
||||
"id",
|
||||
"name",
|
||||
"user",
|
||||
"host",
|
||||
"port",
|
||||
},
|
||||
[]string{"tenant"},
|
||||
),
|
||||
}
|
||||
registerV2(&Routers)
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
// 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 cloudnet
|
||||
|
||||
import (
|
||||
"yunion.io/x/onecloud/pkg/mcclient/modulebase"
|
||||
)
|
||||
|
||||
type RouteManager struct {
|
||||
modulebase.ResourceManager
|
||||
}
|
||||
|
||||
var (
|
||||
Routes RouteManager
|
||||
)
|
||||
|
||||
func init() {
|
||||
Routes = RouteManager{
|
||||
NewCloudnetManager(
|
||||
"route",
|
||||
"routes",
|
||||
[]string{
|
||||
"id",
|
||||
"name",
|
||||
"iface_id",
|
||||
"ifname",
|
||||
"network",
|
||||
"gateway",
|
||||
},
|
||||
[]string{"tenant"},
|
||||
),
|
||||
}
|
||||
registerV2(&Routes)
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
// 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 cloudnet
|
||||
|
||||
import (
|
||||
"yunion.io/x/onecloud/pkg/mcclient/modulebase"
|
||||
)
|
||||
|
||||
type RuleManager struct {
|
||||
modulebase.ResourceManager
|
||||
}
|
||||
|
||||
var (
|
||||
Rules RuleManager
|
||||
)
|
||||
|
||||
func init() {
|
||||
Rules = RuleManager{
|
||||
NewCloudnetManager(
|
||||
"rule",
|
||||
"rules",
|
||||
[]string{
|
||||
"id",
|
||||
"name",
|
||||
"router_id",
|
||||
"match_src_net",
|
||||
"match_dest_net",
|
||||
"match_proto",
|
||||
"match_port",
|
||||
"match_in_ifname",
|
||||
"match_out_ifname",
|
||||
"action",
|
||||
"action_options",
|
||||
},
|
||||
[]string{"tenant"},
|
||||
),
|
||||
}
|
||||
registerV2(&Rules)
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
package cloudnet // import "yunion.io/x/onecloud/pkg/mcclient/options/cloudnet"
|
||||
@@ -0,0 +1,44 @@
|
||||
// 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 cloudnet
|
||||
|
||||
import (
|
||||
"yunion.io/x/onecloud/pkg/mcclient/options"
|
||||
)
|
||||
|
||||
type MeshNetworkCreateOptions struct {
|
||||
NAME string
|
||||
}
|
||||
|
||||
type MeshNetworkGetOptions struct {
|
||||
ID string `json:"-"`
|
||||
}
|
||||
|
||||
type MeshNetworkUpdateOptions struct {
|
||||
ID string `json:"-"`
|
||||
Name string
|
||||
}
|
||||
|
||||
type MeshNetworkDeleteOptions struct {
|
||||
ID string `json:"-"`
|
||||
}
|
||||
|
||||
type MeshNetworkListOptions struct {
|
||||
options.BaseListOptions
|
||||
}
|
||||
|
||||
type MeshNetworkActionRealizeOptions struct {
|
||||
ID string `json:"-"`
|
||||
}
|
||||
@@ -0,0 +1,120 @@
|
||||
// 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 cloudnet
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
|
||||
"yunion.io/x/jsonutils"
|
||||
|
||||
"yunion.io/x/onecloud/pkg/mcclient/options"
|
||||
)
|
||||
|
||||
type RouterCreateOptions struct {
|
||||
NAME string
|
||||
User string
|
||||
Host string
|
||||
Port int
|
||||
PrivateKey string
|
||||
|
||||
RealizeWgIfaces string `choices:"on|off" default:"on" help:"apply wg ifaces config on realization"`
|
||||
RealizeRoutes string `choices:"on|off" default:"on" help:"apply routes config on realization"`
|
||||
RealizeRules string `choices:"on|off" default:"on" help:"apply firewall rules on realization"`
|
||||
}
|
||||
|
||||
func (opts *RouterCreateOptions) Params() (jsonutils.JSONObject, error) {
|
||||
params, err := options.StructToParams(opts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if opts.PrivateKey != "" && opts.PrivateKey[0] == '@' {
|
||||
data, err := ioutil.ReadFile(opts.PrivateKey[1:])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
params.Set("private_key", jsonutils.NewString(string(data)))
|
||||
}
|
||||
return params, nil
|
||||
}
|
||||
|
||||
type RouterGetOptions struct {
|
||||
ID string `json:"-"`
|
||||
}
|
||||
|
||||
type RouterUpdateOptions struct {
|
||||
ID string `json:"-"`
|
||||
Name string
|
||||
|
||||
User string
|
||||
Host string
|
||||
Port int `json:",omitzero"`
|
||||
PrivateKey string
|
||||
|
||||
RealizeWgIfaces string `json:",omitzero" choices:"on|off" help:"apply wg ifaces config on realization"`
|
||||
RealizeRoutes string `json:",omitzero" choices:"on|off" help:"apply routes config on realization"`
|
||||
RealizeRules string `json:",omitzero" choices:"on|off" help:"apply firewall rules on realization"`
|
||||
}
|
||||
|
||||
func (opts *RouterUpdateOptions) Params() (jsonutils.JSONObject, error) {
|
||||
params, err := options.StructToParams(opts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if opts.PrivateKey != "" && opts.PrivateKey[0] == '@' {
|
||||
data, err := ioutil.ReadFile(opts.PrivateKey[1:])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
params.Set("private_key", jsonutils.NewString(string(data)))
|
||||
}
|
||||
return params, nil
|
||||
}
|
||||
|
||||
type RouterDeleteOptions struct {
|
||||
ID string `json:"-"`
|
||||
}
|
||||
|
||||
type RouterListOptions struct {
|
||||
options.BaseListOptions
|
||||
}
|
||||
|
||||
type RouterActionJoinMeshNetworkOptions struct {
|
||||
ID string `json:"-"`
|
||||
|
||||
MeshNetwork string
|
||||
AdvertiseSubnets string `help:"cidr concatenated by comma"`
|
||||
}
|
||||
|
||||
type RouterActionLeaveMeshNetworkOptions struct {
|
||||
ID string `json:"-"`
|
||||
|
||||
MeshNetwork string
|
||||
}
|
||||
|
||||
type RouterActionRegisterIfnameOptions struct {
|
||||
ID string `json:"-"`
|
||||
|
||||
Ifname string
|
||||
}
|
||||
|
||||
type RouterActionUnregisterIfnameOptions struct {
|
||||
ID string `json:"-"`
|
||||
|
||||
Ifname string
|
||||
}
|
||||
|
||||
type RouterActionRealizeOptions struct {
|
||||
ID string `json:"-"`
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
// 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 cloudnet
|
||||
|
||||
import (
|
||||
"yunion.io/x/onecloud/pkg/mcclient/options"
|
||||
)
|
||||
|
||||
type RouteCreateOptions struct {
|
||||
NAME string
|
||||
|
||||
Iface string `required:"true"`
|
||||
Network string
|
||||
Gateway string
|
||||
}
|
||||
|
||||
type RouteGetOptions struct {
|
||||
ID string `json:"-"`
|
||||
}
|
||||
|
||||
type RouteUpdateOptions struct {
|
||||
ID string `json:"-"`
|
||||
Name string
|
||||
|
||||
Network string
|
||||
Gateway string
|
||||
}
|
||||
|
||||
type RouteDeleteOptions struct {
|
||||
ID string `json:"-"`
|
||||
}
|
||||
|
||||
type RouteListOptions struct {
|
||||
options.BaseListOptions
|
||||
|
||||
Iface string
|
||||
Network string
|
||||
Gateway string
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
// 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 cloudnet
|
||||
|
||||
import (
|
||||
"yunion.io/x/onecloud/pkg/mcclient/options"
|
||||
)
|
||||
|
||||
type RuleCreateOptions struct {
|
||||
NAME string
|
||||
|
||||
Router string `required:"true"`
|
||||
|
||||
MatchSrcNet string
|
||||
MatchDestNet string
|
||||
MatchProto string
|
||||
MatchSrcPort int
|
||||
MatchDestPort int
|
||||
MatchInIfname string
|
||||
MatchOutIfname string
|
||||
|
||||
Action string
|
||||
ActionOptions string
|
||||
}
|
||||
|
||||
type RuleGetOptions struct {
|
||||
ID string `json:"-"`
|
||||
}
|
||||
|
||||
type RuleUpdateOptions struct {
|
||||
ID string `json:"-"`
|
||||
Name string
|
||||
|
||||
MatchSrcNet string
|
||||
MatchDestNet string
|
||||
MatchProto string
|
||||
MatchSrcPort int `json:",omitzero"`
|
||||
MatchDestPort int `json:",omitzero"`
|
||||
MatchInIfname string
|
||||
MatchOutIfname string
|
||||
|
||||
Action string
|
||||
ActionOptions string
|
||||
}
|
||||
|
||||
type RuleDeleteOptions struct {
|
||||
ID string `json:"-"`
|
||||
}
|
||||
|
||||
type RuleListOptions struct {
|
||||
options.BaseListOptions
|
||||
|
||||
Router string
|
||||
|
||||
MatchSrcNet string
|
||||
MatchDestNet string
|
||||
MatchProto string
|
||||
MatchSrcPort int `json:",omitzero"`
|
||||
MatchDestPort int `json:",omitzero"`
|
||||
MatchInIfname string
|
||||
MatchOutIfname string
|
||||
|
||||
Action string
|
||||
ActionOptions string
|
||||
}
|
||||
Reference in New Issue
Block a user