blob: 834568e1e0bec203e9c9dc053abcd3740733d417 [file] [log] [blame]
#!/usr/bin/env python
#
# Copyright (c) 2016-2017 Nest Labs, Inc.
# All rights reserved.
#
# 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.
#
#
# @file
# Implements WeaveTunnelStart class that start Weave Tunnel
# between border-gateway and a service.
#
import os
import sys
import time
from happy.ReturnMsg import ReturnMsg
from happy.Utils import *
from happy.HappyNetwork import HappyNetwork
from happy.HappyNode import HappyNode
import happy.HappyNodeRoute
import happy.HappyNetworkAddress
import plugin.WeaveTunnelStop as WeaveTunnelStop
from plugin.WeaveTest import WeaveTest
import plugin.WeaveUtilities as WeaveUtilities
options = {"border_gateway": None,
"service": None,
"tap": None,
"primary": None,
"backup": None,
"customized_tunnel_port": None,
"service_dir": False,
"skip_service_end": False,
"tunnel_log": True,
"case": False,
"service_dir_server": None,
"service_process_tag": "WEAVE-SERVICE-TUNNEL",
"gateway_process_tag": "WEAVE-GATEWAY-TUNNEL",
"case_cert_path": None,
"case_key_path": None,
"test_tag": "",
"iterations": 1,
"gateway_faults": None,
"service_faults": None,
"strace": False,
"sync_on_gateway_output": "",
"sync_on_service_output": "",
"product_id": "12", # kNestWeaveProduct_Flintstone
"plaid_gateway_env": {},
"plaid_service_env": {}
}
def option():
return options.copy()
class WeaveTunnelStart(HappyNode, HappyNetwork, WeaveTest):
"""
weave-tunnel-start start a tunnel between border-gateway and a service
weave-tunnel-start [-h --help] [-q --quiet] [-b --border_gateway <NODE_ID>]
[-s --service <NODE_ID> | <IP_ADDR>] [-p --tap <TAP_INTERFACE>] [-P --primary <PRIMARY_INTERFACE>] [-B --backup <BACKUP_INTERFACE>]
[-d --service_dir] [-i --service_dir_server <SERVICE_HOST>] [-C --case] [-E --case_cert_path <path>] [-T --case_key_path <path>]
Example:
$ weave-tunnel-start node01 node02
Start weave tunnel between border-gateway node01 and service node02.
$ weave-tunnel-start node01 184.72.129.8
Start weave tunnel between border-gateway node01 and a node with an IP address 184.72.129.8.
$ weave-tunnel-start --border_gateway node01 --service_dir --service_dir_server "frontdoor.integration.nestlabs.com" --case --case_cert_path path --case_key_path path
Start weave tunnel with case encryption between border-gateway node01 and a node with the service loopup directory frontdoor.integration.nestlabs.com.
$ weave-tunnel-start --border_gateway node01 --service 184.72.129.8 -P wlan0 -B cellular0
Start weave tunnel between border-gateway node01 and a node with an IP address 184.72.129.8, the primary tunnel go through wlan0, the backup tunnel
go through cellular0
return:
0 success
1 fail
"""
def __init__(self, opts = options):
HappyNode.__init__(self)
HappyNetwork.__init__(self)
WeaveTest.__init__(self)
self.__dict__.update(opts)
emsg = "starting with opts: %s" %opts
self.logger.debug("[localhost] WeaveTunnelStart: %s" %emsg)
self.gateway_process_tag += self.test_tag
self.service_process_tag += self.test_tag
def __stopExistingTunnel(self):
options = WeaveTunnelStop.option()
options["border_gateway"] = self.border_gateway
options["service"] = self.service
stopTunnel = WeaveTunnelStop.WeaveTunnelStop(options)
stopTunnel.run()
self.readState()
def __pre_check(self):
# Check if border-gateway node was given
if not self.border_gateway:
emsg = "Border-gateway node id not specified."
self.logger.error("[localhost] WeaveTunnelStart: %s" % (emsg))
self.exit()
if not self._nodeExists(self.border_gateway):
emsg = "Border-gateway node %s does not exist." % (self.border_gateway)
self.logger.error("[localhost] WeaveTunnelStart: %s" % (emsg))
self.exit()
# Check if service node was given in the environment
if not self.service and not self.service_dir:
if "weave_service_address" in os.environ.keys():
self.service = os.environ['weave_service_address']
emsg = "Found weave_service_address %s." % (self.service)
self.logger.debug("[localhost] Weave: %s" % (emsg))
if not self.service and self.service_dir:
if self.service_dir_server:
self.skip_service_end = True
self.logger.debug("[localhost] WeaveTunnelStart against tier %s." % self.service_dir_server)
else:
if "weave_service_address" in os.environ.keys():
self.skip_service_end = True
self.service_dir_server = os.environ['weave_service_address']
self.logger.debug("[localhost] WeaveTunnelStart against tier %s." % self.service_dir_server)
else:
# Check if service node was given
emsg = "Service node id (or IP address or service directory) not specified."
self.logger.error("[localhost] WeaveTunnelStart: %s" % (emsg))
self.exit()
if self.service:
# If service is a domain name, convert it to IP
if self.isDomainName(self.service):
ip = self.getHostByName(self.service)
self.service = ip
if not self.isIpAddress(self.service) and not self._nodeExists(self.service):
emsg = "Service node %s does not exist." % (self.service)
self.logger.error("[localhost] WeaveTunnelStart: %s" % (emsg))
self.exit()
if self.isIpAddress(self.service):
self.service_ip = self.service
self.skip_service_end = True
else:
self.service_ip = self.getNodePublicIPv4Address(self.service)
if self.service_ip == None:
emsg = "Could not find IP address of the service node."
self.logger.error("[localhost] WeaveTunnel: %s" % (emsg))
self.exit()
self.fabric_id = self.getFabricId()
# Check if there is no fabric
if self.fabric_id == None:
emsg = "There is not Weave fabric."
self.logger.error("[localhost] WeaveTunnelStart: %s" % (emsg))
self.exit()
# Check if there is a tunnel
if self.getTunnelServiceNodeId() or self.getTunnelServiceDir():
emsg = "There already exist a Weave tunnel."
self.logger.error("[localhost] WeaveTunnelStart: %s" % (emsg))
self.__stopExistingTunnel()
self.service_weave_id = self.getServiceWeaveID("Tunnel")
self.gateway_weave_id = self.getWeaveNodeID(self.border_gateway)
if self.service_weave_id == None:
emsg = "Could not find Weave node ID of the service node."
self.logger.error("[localhost] WeaveTunnel: %s" % (emsg))
self.exit()
if self.gateway_weave_id == None:
emsg = "Could not find Weave node ID of the border-gateway node."
self.logger.error("[localhost] WeaveTunnel: %s" % (emsg))
self.exit()
def __start_tunnel_at_service(self):
cmd = self.getWeaveTunnelServicePath()
if not cmd:
return
cmd += " --node-id " + str(self.service_weave_id)
cmd += " --fabric-id " + str(self.fabric_id)
if self.tap:
cmd += " --interface " + self.tap
if self.service_faults:
cmd += " --faults " + self.service_faults
cmd += " --iterations " + str(self.iterations)
cmd += " --debug-resource-usage"
cmd += " --print-fault-counters"
cmd = self.runAsRoot(cmd)
self.start_weave_process(self.service, cmd, self.service_process_tag, strace=self.strace, sync_on_output=self.sync_on_service_output, env=self.plaid_service_env)
max_wait_time = 40
while not self._nodeInterfaceExists(self.service_tun, self.service):
time.sleep(0.1)
max_wait_time -= 1
if max_wait_time <= 0:
emsg = "Service-side tunnel interface %s was not created." % (self.service_tun)
self.logger.error("[%s] WeaveTunnel: %s" % (self.service, emsg))
self.exit()
if (max_wait_time) % 10 == 0:
emsg = "Waiting for interface %s to be created." % (self.service_tun)
self.logger.debug("[%s] WeaveTunnel: %s" % (self.service, emsg))
with self.getStateLockManager():
self.readState()
new_node_interface = {}
new_node_interface["link"] = None
new_node_interface["type"] = self.network_type["tun"]
new_node_interface["ip"] = {}
self.setNodeInterface(self.service, self.service_tun, new_node_interface)
self.writeState()
def __assing_tunnel_endpoint_ula_at_service(self):
addr = self.getServiceWeaveIPAddress("Tunnel", self.service)
options = happy.HappyNodeAddress.option()
options["node_id"] = self.service
options["interface"] = self.service_tun
options["address"] = addr
options["add"] = True
addAddr = happy.HappyNodeAddress.HappyNodeAddress(options)
addAddr.run()
def __start_tunnel_at_gateway(self):
cmd = self.getWeaveTunnelBorderGatewayPath()
if not cmd:
return
cmd += " --node-id " + self.gateway_weave_id
cmd += " --fabric-id " + str(self.fabric_id)
if self.tap:
cmd += " --interface " + self.tap
if self.primary:
cmd += " --primary-intf " + self.primary
if self.backup:
cmd += " --backup-intf " + self.backup + " --enable-backup "
if self.tunnel_log:
cmd += " --tunnel-log"
if self.product_id:
cmd += " --product-id " + self.product_id
if self.case:
cmd += ' --case'
if not self.case_cert_path == 'default':
self.cert_file = self.case_cert_path if self.case_cert_path else os.path.join(self.main_conf['log_directory'], self.gateway_weave_id.upper() + '-cert.weave-b64')
self.key_file = self.case_key_path if self.case_key_path else os.path.join(self.main_conf['log_directory'], self.gateway_weave_id.upper() + '-key.weave-b64')
cmd += ' --node-cert ' + self.cert_file + ' --node-key ' + self.key_file
if self.service_dir:
cmd += " --service-dir " + str(self.service_weave_id) + ' --service-dir-server ' + self.service_dir_server
else:
cmd += " --connect-to " + self.service_ip + " " + str(self.service_weave_id)
elif self.service:
if self.customized_tunnel_port:
self.service_ip = self.service_ip + ":%d" % self.customized_tunnel_port
cmd += " --connect-to " + self.service_ip + " " + str(self.service_weave_id)
# fault-injection related
cmd += " --debug-resource-usage"
cmd += " --print-fault-counters"
if self.gateway_faults:
cmd += " --faults " + self.gateway_faults
cmd += " --iterations " + str(self.iterations)
cmd = self.runAsRoot(cmd)
self.start_weave_process(self.border_gateway, cmd, self.gateway_process_tag, strace=self.strace, sync_on_output=self.sync_on_gateway_output, env=self.plaid_gateway_env)
def __post_check(self):
tries = 100
while not self._nodeInterfaceExists(self.gateway_tun, self.border_gateway):
self.logger.debug("[localhost] WeaveTunnelStart: sleeping in __post_check")
time.sleep(1)
tries -= 1
if tries < 0:
emsg = "Tunnel interface %s cannot be found at node %s." % (self.gateway_tun, self.border_gateway)
self.logger.error("[localhost] WeaveTunnelStart: %s" % (emsg))
self.exit()
def __add_tunnel_state(self):
new_tunnel = {}
new_tunnel["gateway"] = self.border_gateway
new_tunnel["service"] = self.service
new_tunnel["service_dir"] = self.service_dir_server
self.setTunnel(new_tunnel)
def __add_tunnel_route(self):
self.global_prefix = self.getFabricGlobalPrefix()
options = happy.HappyNodeRoute.option()
options["node_id"] = self.border_gateway
options["to"] = self.global_prefix
options["via"] = self.gateway_tun
options["prefix"] = self.global_prefix
options["add"] = True
self.logger.debug("[localhost] WeaveTunnelStart: adding route with options: " + str(options) )
weaveServiceRoute = happy.HappyNodeRoute.HappyNodeRoute(options)
weaveServiceRoute.start()
def run(self):
self.logger.debug("[localhost] WeaveTunnelStart: Run.")
with self.getStateLockManager():
self.__pre_check()
if not self.skip_service_end:
self.__start_tunnel_at_service()
self.__assing_tunnel_endpoint_ula_at_service()
self.__start_tunnel_at_gateway()
with self.getStateLockManager():
self.readState()
self.__post_check()
self.__add_tunnel_state()
self.writeState()
self.__add_tunnel_route()
data = {}
gateway_output_value, gateway_output_data = \
self.get_test_output(self.border_gateway, self.gateway_process_tag, quiet=True)
if self.strace:
gateway_strace_value, gateway_strace_data = \
self.get_test_strace(self.border_gateway, self.gateway_process_tag)
data["gateway_strace"] = gateway_strace_data
if self.skip_service_end:
service_output_data = ""
service_strace_data = ""
service_output_value = 0
else:
service_output_value, service_output_data = \
self.get_test_output(self.service, self.service_process_tag, quiet=True)
if self.strace:
service_strace_value, service_strace_data = \
self.get_test_strace(self.service, self.service_process_tag)
data["service_strace"] = service_strace_data
data["gateway_output"] = gateway_output_data
data["service_output"] = service_output_data
result = gateway_output_value or service_output_value # 0 == success, hence 'or'
self.logger.debug("[localhost] WeaveTunnelStart: Done.")
return ReturnMsg(result, data)