Files
cloudpods/pkg/cloudnet/models/routers_ansible.go
2019-10-18 03:40:53 +00:00

368 lines
9.6 KiB
Go

// Copyright 2019 Yunion
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package models
import (
"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
}