# -*- coding:utf-8 -*-
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You 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 logging
from networkapiclient.xml_utils import dumps_networkapi
from networkapiclient.xml_utils import loads
try:
from urllib2 import *
except:
from urllib.request import *
try:
from urlparse import urlparse
except:
from urllib.parse import urlparse
try:
from httplib import *
except:
from http.client import *
LOG = logging.getLogger('networkapiclient.rest')
[docs]class RestError(Exception):
"""Representa um erro ocorrido durante uma requisão REST."""
def __init__(self, cause, message):
self.cause = cause
self.message = message
def __str__(self):
msg = u'Erro ao realizar requisição REST: Causa: %s, Mensagem: %s' % (
self.cause, self.message)
return msg.encode('utf-8')
[docs]class ConnectionError(RestError):
"""Caso ocorra algum erro de conexão com a NetworkAPI."""
def __init__(self, cause):
super(
ConnectionError,
self).__init__(
cause,
u'Falha na conexão com a NetworkAPI.')
[docs]class Rest:
"""Classe utilitária para chamada de webservices REST.
Implementa métodos utilitários para realizações de chamadas a webservices
REST.
"""
def __init__(self):
proxy_support = ProxyHandler({})
opener = build_opener(proxy_support)
install_opener(opener)
[docs] def get(self, url, auth_map=None):
"""Envia uma requisição GET para a URL informada.
Se auth_map é diferente de None, então deverá conter as
chaves NETWORKAPI_PASSWORD e NETWORKAPI_USERNAME para realizar
a autenticação na networkAPI.
As chaves e os seus valores são enviados no header da requisição.
:param url: URL para enviar a requisição HTTP.
:param auth_map: Dicionário com as informações para autenticação na networkAPI.
:return: Retorna uma tupla contendo:
(< código de resposta http >, < corpo da resposta >).
:raise ConnectionError: Falha na conexão com a networkAPI.
:raise RestError: Falha no acesso à networkAPI.
"""
try:
LOG.debug('GET %s', url)
request = Request(url)
if auth_map is not None:
for key in auth_map.iterkeys():
request.add_header(key, auth_map[key])
# request.add_header('NETWORKAPI_PASSWORD', auth_map['NETWORKAPI_PASSWORD'])
# request.add_header('NETWORKAPI_USERNAME', auth_map['NETWORKAPI_USERNAME'])
content = urlopen(request).read()
response_code = 200
LOG.debug('GET %s returns %s\n%s', url, response_code, content)
return response_code, content
except HTTPError as e:
response_code = e.code
content = ''
if int(e.code) == 500:
content = e.read()
LOG.debug('GET %s returns %s\n%s', url, response_code, content)
return response_code, content
except URLError as e:
raise ConnectionError(e)
except Exception as e:
raise RestError(e, e.message)
[docs] def post(self, url, request_data, content_type=None, auth_map=None):
"""Envia uma requisição POST para a URL informada.
Se auth_map é diferente de None, então deverá conter as
chaves NETWORKAPI_PASSWORD e NETWORKAPI_USERNAME para realizar
a autenticação na networkAPI.
As chaves e os seus valores são enviados no header da requisição.
:param url: URL para enviar a requisição HTTP.
:param request_data: Descrição para enviar no corpo da requisição HTTP.
:param content_type: Tipo do conteúdo enviado em request_data. O valor deste
parâmetro será adicionado no header "Content-Type" da requisição.
:param auth_map: Dicionário com as informações para autenticação na networkAPI.
:return: Retorna uma tupla contendo:
::
(< código de resposta http >, < corpo da resposta >).
:raise ConnectionError: Falha na conexão com a networkAPI.
:raise RestError: Falha no acesso à networkAPI.
"""
try:
LOG.debug('POST %s\n%s', url, request_data)
request = Request(url)
request.add_data(request_data)
# print request_data
if auth_map is not None:
for key in auth_map.iterkeys():
request.add_header(key, auth_map[key])
# request.add_header('NETWORKAPI_PASSWORD', auth_map['NETWORKAPI_PASSWORD'])
# request.add_header('NETWORKAPI_USERNAME', auth_map['NETWORKAPI_USERNAME'])
if content_type is not None:
request.add_header('Content-Type', content_type)
content = urlopen(request).read()
response_code = 200
LOG.debug('POST %s returns %s\n%s', url, response_code, content)
return response_code, content
except HTTPError as e:
response_code = e.code
content = ''
if int(e.code) == 500:
content = e.read()
LOG.debug('POST %s returns %s\n%s', url, response_code, content)
return response_code, content
except URLError as e:
raise ConnectionError(e)
except Exception as e:
raise RestError(e, e.message)
[docs] def delete(self, url, request_data, content_type=None, auth_map=None):
"""Envia uma requisição DELETE para a URL informada.
Se auth_map é diferente de None, então deverá conter as
chaves NETWORKAPI_PASSWORD e NETWORKAPI_USERNAME para realizar
a autenticação na networkAPI.
As chaves e os seus valores são enviados no header da requisição.
:param url: URL para enviar a requisição HTTP.
:param request_data: Descrição para enviar no corpo da requisição HTTP.
:param content_type: Tipo do conteúdo enviado em request_data. O valor deste
parâmetro será adicionado no header "Content-Type" da requisição.
:param auth_map: Dicionário com as informações para autenticação na networkAPI.
:return: Retorna uma tupla contendo:
(< código de resposta http >, < corpo da resposta >).
:raise ConnectionError: Falha na conexão com a networkAPI.
:raise RestError: Falha no acesso à networkAPI.
"""
try:
LOG.debug('DELETE %s', url)
parsed_url = urlparse(url)
if parsed_url.scheme == 'https':
connection = HTTPSConnection(
parsed_url.hostname,
parsed_url.port)
else:
connection = HTTPConnection(
parsed_url.hostname,
parsed_url.port)
try:
headers_map = dict()
if auth_map is not None:
headers_map.update(auth_map)
if content_type is not None:
headers_map['Content-Type'] = content_type
connection.request(
'DELETE',
self.get_full_url(parsed_url),
request_data,
headers_map)
response = connection.getresponse()
body = response.read()
LOG.debug(
'DELETE %s returns %s\n%s',
url,
response.status,
body)
return response.status, body
finally:
connection.close()
except URLError as e:
raise ConnectionError(e)
except Exception as e:
raise RestError(e, e.message)
[docs] def put(self, url, request_data, content_type=None, auth_map=None):
"""Envia uma requisição PUT para a URL informada.
Se auth_map é diferente de None, então deverá conter as
chaves NETWORKAPI_PASSWORD e NETWORKAPI_USERNAME para realizar
a autenticação na networkAPI.
As chaves e os seus valores são enviados no header da requisição.
:param url: URL para enviar a requisição HTTP.
:param request_data: Descrição para enviar no corpo da requisição HTTP.
:param content_type: Tipo do conteúdo enviado em request_data. O valor deste
parâmetro será adicionado no header "Content-Type" da requisição.
:param auth_map: Dicionário com as informações para autenticação na networkAPI.
:return: Retorna uma tupla contendo:
::
(< código de resposta http >, < corpo da resposta >).
:raise ConnectionError: Falha na conexão com a networkAPI.
:raise RestError: Falha no acesso à networkAPI.
"""
try:
LOG.debug('PUT %s\n%s', url, request_data)
parsed_url = urlparse(url)
if parsed_url.scheme == 'https':
connection = HTTPSConnection(
parsed_url.hostname,
parsed_url.port)
else:
connection = HTTPConnection(
parsed_url.hostname,
parsed_url.port)
try:
headers_map = dict()
if auth_map is not None:
headers_map.update(auth_map)
if content_type is not None:
headers_map['Content-Type'] = content_type
connection.request(
'PUT',
parsed_url.path,
request_data,
headers_map)
response = connection.getresponse()
body = response.read()
LOG.debug('PUT %s returns %s\n%s', url, response.status, body)
return response.status, body
finally:
connection.close()
except URLError as e:
raise ConnectionError(e)
except Exception as e:
raise RestError(e, e.message)
[docs] def post_map(self, url, map, auth_map=None):
"""Gera um XML a partir dos dados do dicionário e o envia através de uma requisição POST.
:param url: URL para enviar a requisição HTTP.
:param map: Dicionário com os dados do corpo da requisição HTTP.
:param auth_map: Dicionário com as informações para autenticação na networkAPI.
:return: Retorna uma tupla contendo:
(< código de resposta http >, < corpo da resposta >).
:raise ConnectionError: Falha na conexão com a networkAPI.
:raise RestError: Falha no acesso à networkAPI.
"""
xml = dumps_networkapi(map)
response_code, content = self.post(url, xml, 'text/plain', auth_map)
return response_code, content
[docs] def put_map(self, url, map, auth_map=None):
"""Gera um XML a partir dos dados do dicionário e o envia através de uma requisição PUT.
:param url: URL para enviar a requisição HTTP.
:param map: Dicionário com os dados do corpo da requisição HTTP.
:param auth_map: Dicionário com as informações para autenticação na networkAPI.
:return: Retorna uma tupla contendo:
(< código de resposta http>, < corpo da resposta>).
:raise ConnectionError: Falha na conexão com a networkAPI.
:raise RestError: Falha no acesso à networkAPI.
"""
xml = dumps_networkapi(map)
response_code, content = self.put(url, xml, 'text/plain', auth_map)
return response_code, content
[docs] def get_map(self, url, auth_map=None):
"""Envia uma requisição GET.
:param url: URL para enviar a requisição HTTP.
:param auth_map: Dicionário com as informações para autenticação na networkAPI.
:return: Retorna uma tupla contendo:
(< código de resposta http >, < corpo da resposta >).
:raise ConnectionError: Falha na conexão com a networkAPI.
:raise RestError: Falha no acesso à networkAPI.
"""
response_code, content = self.get(url, auth_map)
return response_code, content
[docs] def delete_map(self, url, map=None, auth_map=None):
"""Gera um XML a partir dos dados do dicionário e o envia através de uma requisição DELETE.
:param url: URL para enviar a requisição HTTP.
:param map: Dicionário com os dados do corpo da requisição HTTP.
:param auth_map: Dicionário com as informações para autenticação na networkAPI.
:return: Retorna uma tupla contendo:
(< código de resposta http >, < corpo da resposta >).
:raise ConnectionError: Falha na conexão com a networkAPI.
:raise RestError: Falha no acesso à networkAPI.
"""
xml = None
if map is not None:
xml = dumps_networkapi(map)
response_code, content = self.delete(url, xml, 'text/plain', auth_map)
return response_code, content
[docs] def unmarshall(self, content):
try:
return loads(content)
except Exception as e:
raise RestError(e, u'Erro ao gerar o mapa de resposta!\n'
u'Conteúdo recebido:\n%s' % content)
[docs] def get_full_url(self, parsed_url):
""" Returns url path with querystring """
full_path = parsed_url.path
if parsed_url.query:
full_path = '%s?%s' % (full_path, parsed_url.query)
return full_path
[docs]class RestRequest:
"""Classe básica para requisições webservices REST à networkAPI"""
def __init__(self, url, method, user, password, user_ldap=None):
'''Construtor da classe.
:param url: URL para enviar a requisição HTTP.
:param method: Método da requisição ('POST', 'PUT', 'GET' ou 'DELETE').
:param user: Usuário para autenticação na networkAPI.
:param password: Senha para autenticação na networkAPI.
'''
self.url = url
self.method = method
self.auth_map = dict()
self.auth_map['NETWORKAPI_USERNAME'] = user
self.auth_map['NETWORKAPI_PASSWORD'] = password
if user_ldap is not None:
self.auth_map['NETWORKAPI_USERLDAP'] = user_ldap
[docs] def submit(self, map):
'''Envia a requisição HTTP de acordo com os parâmetros informados no construtor.
:param map: Dicionário com os dados do corpo da requisição.
:return: Retorna uma tupla contendo:
(< código de resposta http >, < corpo da resposta >).
:raise ConnectionError: Falha na conexão com a networkAPI.
:raise RestError: Falha no acesso à networkAPI.
'''
# print "Requição em %s %s com corpo: %s" % (self.method, self.url,
# map)
rest = Rest()
if self.method == 'POST':
code, response = rest.post_map(self.url, map, self.auth_map)
elif self.method == 'PUT':
code, response = rest.put_map(self.url, map, self.auth_map)
elif self.method == 'GET':
code, response = rest.get_map(self.url, self.auth_map)
elif self.method == 'DELETE':
code, response = rest.delete_map(self.url, map, self.auth_map)
return code, response