blob: 5bcf5647927c586f4c1bd8c146601be653fabfb6 [file] [log] [blame]
#!/usr/bin/env python
#
# Copyright (c) 2015-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 generate register service cmd which is used to service provisioning
import json
import os
import requests
import shutil
import sys
import tarfile
from grpc.beta import implementations
from grpc.framework.interfaces.face.face import ExpirationError
from httplib import HTTPSConnection
from happy.Driver import Driver
from happy.Utils import *
options = {}
options["tier"] = None
options["username"] = None
options["password"] = None
def option():
return options.copy()
default_schema_branch = 'develop'
apigw_fmt = 'apigw01.weave01.iad02.{tier}.nestlabs.com'
phoenix_mirror_url = 'http://device-automation.nestlabs.com/phoenix-schema/python'
python_local = os.path.expanduser('~/.python-local')
phoenix_cache = os.path.join(python_local, 'phoenix')
def get_phoenix_hash(branch):
url = '{}/{}/__latest__'.format(phoenix_mirror_url, branch)
response = requests.get(url)
response.raise_for_status()
return response.text.rstrip()
_phoenix_proto_cache = dict()
def get_phoenix_proto_dir(branch):
if branch in _phoenix_proto_cache:
return _phoenix_proto_cache.get(branch)
latest_hash = get_phoenix_hash(branch)
tarball_dir = os.path.join(phoenix_cache, branch)
tarball_path = os.path.join(tarball_dir, '{}.tar.gz'.format(latest_hash))
extract_dir = os.path.join(tarball_dir, latest_hash)
phoenix_dir = os.path.join(extract_dir, 'phoenix')
if not os.path.exists(extract_dir):
os.makedirs(extract_dir)
if not os.path.exists(tarball_path):
url = '{}/{}/{}.tar.gz'.format(phoenix_mirror_url, branch, latest_hash)
response = requests.get(url, stream=True)
response.raise_for_status()
with open(tarball_path, 'wb') as tarball_file:
print type(response.raw)
shutil.copyfileobj(response.raw, tarball_file)
if not os.path.exists(phoenix_dir):
with tarfile.open(tarball_path) as tar:
for member in tar.getmembers():
if not member.isdir():
tar.extract(member.path, path=extract_dir)
_phoenix_proto_cache[branch] = phoenix_dir
return phoenix_dir
def use_phoenix_schema(branch):
sys.path.insert(0, get_phoenix_proto_dir(branch))
class ServiceClient(object):
def __init__(self, tier, username, password, token,
schema_branch=default_schema_branch):
self.tier = tier
self.username = username
self.password = password
use_phoenix_schema(schema_branch)
import nestlabs.gateway.v2.gateway_api_pb2 as gateway_api
self.gateway_api = gateway_api
auth_token = 'Basic ' + token
self._auth_metadata = [('authorization', auth_token)]
def _create_gateway_service_stub(self):
apigw = apigw_fmt.format(tier=self.tier)
return \
self.gateway_api.beta_create_GatewayService_stub(
implementations.insecure_channel(apigw, 9953))
@property
def account_id(self):
gateway_api = self.gateway_api
request = gateway_api.ObserveRequest()
request.state_types.append(gateway_api.StateType.Value('ACCEPTED'))
request.state_types.append(gateway_api.StateType.Value('CONFIRMED'))
stub = self._create_gateway_service_stub()
for response in stub.Observe(request, 999999, self._auth_metadata):
for resource_meta in response.resource_metas:
if 'USER' in resource_meta.resource_id:
return resource_meta.resource_id.encode('utf-8')
if not response.initial_resource_metas_continue:
break
@property
def structure_ids(self):
gateway_api = self.gateway_api
request = gateway_api.ObserveRequest()
request.state_types.append(gateway_api.StateType.Value('ACCEPTED'))
request.state_types.append(gateway_api.StateType.Value('CONFIRMED'))
stub = self._create_gateway_service_stub()
ids = []
try:
for response in stub.Observe(request, 15, self._auth_metadata):
for resource_meta in response.resource_metas:
if 'STRUCTURE' in resource_meta.resource_id:
ids.append(resource_meta.resource_id.encode('utf-8'))
if not response.initial_resource_metas_continue:
break
except ExpirationError:
pass
finally:
return ids
class WeaveRegisterService(Driver):
"""
weave-register-service [-h --help] [-q --quiet] [-t --tier <NAME>] [-u --username <NAME>] [-p --password <password>]
--tier option is the service tier
command to generate options of register-service cmd:
$ weave-register-serivce -t integration -u username@nestlabs.com -p yourpassword
return:
options of the options of register service command
"""
def __init__(self, opts = options):
Driver.__init__(self)
self.tier = opts["tier"]
self.username = opts["username"]
self.password = opts["password"]
self.headers = {'Content-Type' : 'application/json'}
def __pre_check(self):
if not self.tier:
self.tier = "integration"
emsg = "WeaveRegisterService: Using default weave_service_tier %s." % (self.tier)
self.logger.debug(emsg)
self.host = 'home.%s.nestlabs.com' % self.tier
# Siac tiers contain 'unstable' and don't expose a 'home.*' hostname.
if 'unstable' in self.tier:
self.host = self.host.replace('home.','')
if not self.username:
self.username = "test-it+pairing1@nestlabs.com"
emsg = "WeaveRegisterService: using default weave_service_username %s." % (self.username)
self.logger.debug(emsg)
if not self.password:
# Check if service password is set
self.password = "nest-egg"
emsg = "WeaveRegisterService: using default weave_service_password %s." % (self.password)
self.logger.debug(emsg)
self.params = json.dumps({'email': self.username, 'username': self.username, 'password':self.password})
self.access_token, self.user_id = self.get_cz_token_userid()
self.sessionJSON = self.__get_session_json()
client = ServiceClient(tier=self.tier, username=self.username, password=self.password, token=self.access_token)
self.structureids = client.structure_ids
self.accountid = client.account_id
self.initial_data = self.__get_initial_data_json()
def get_cz_token_userid(self):
conn = HTTPSConnection(self.host)
path = '/api/0.2/create_user_login'
conn.request('POST', path, self.params, headers=self.headers)
login_response = conn.getresponse()
login_response_data = json.load(login_response)
if login_response.status == 201:
delayExecution(1)
self.logger.info("create account for user %s" % self.username)
token = login_response_data['access_token']
user_id = login_response_data['user']
else:
self.logger.info("get auth info for user %s" % self.username)
token, user_id = self.__get_account_auth()
return token, user_id
def __get_account_auth(self):
conn = HTTPSConnection(self.host)
path = '/api/0.1/authenticate_user'
conn.request('POST', path, self.params, headers=self.headers)
auth_response = conn.getresponse()
auth_response_data = json.load(auth_response)
if auth_response.status == 200:
self.logger.info("WeaveRegisterService: Authentication successful")
elif auth_response.status== 400:
emsg = "WeaveRegisterService: Unauthorized request for user authentication: status=%s error=%s" % (
auth_response.status, auth_response.reason)
self.logger.info(emsg)
raise ValueError(emsg)
else:
# Not a 200 or 4xx auth error, server error.
emsg = "WeaveRegisterService: Service Error on user authentication: HTTPS %s: %s. " % (
auth_response.status, auth_response.reason)
self.logger.info(emsg)
raise ValueError(emsg)
token = auth_response_data['access_token']
user_id = auth_response_data['user']
return token, user_id
def __get_session_json(self):
conn = HTTPSConnection(self.host)
path = '/session'
conn.request('POST', path, self.params, headers=self.headers)
response = conn.getresponse()
data = response.read()
if response.status != 200 and response.status != 201:
emsg = 'WeaveRegisterService: Failed with status %d: %s. Password and login correct?' % (response.status, response.reason)
self.logger.info(emsg)
raise ValueError(emsg)
return json.loads(data)
def __get_initial_data_json(self):
where_id = '00000000-0000-0000-0000-000100000010'
spoken_where_id = '00000000-0000-0000-0000-000100000010'
initialDataJSON = {'structure_id': self.structureids[0], 'where_id': where_id, 'spoken_where_id': spoken_where_id}
return initialDataJSON
def run(self):
self.logger.debug("[localhost] WeaveRegisterService: Run.")
self.__pre_check()
self.cmd = ' --account-id %s --pairing-token %s --service-config %s --init-data \'%s\'' % (self.accountid, self.sessionJSON['weave']['pairing_token'], self.sessionJSON['weave']['service_config'], json.dumps(self.initial_data).encode("UTF-8"))
print "weave-register-service generated the service registration command:"
print hgreen("register-service %s : " % self.cmd)
self.logger.debug("[localhost] WeaveRegisterService: Done.")
return self.cmd