API do Spectrum Virtualize RESTful

A interface de programação de aplicativos (API) do modelo Spectrum Virtualize Representational State Transfer (REST) consiste em destinos de comandos que são usados para recuperar informações do sistema e para criar, modificar e excluir recursos do sistema. Esses destinos de comandos permitem que os parâmetros do comando sejam transmitidos não editados para a interface da linha de comandos do Spectrum Virtualize, que manipula as especificações do parâmetro de análise para validação e relatório de erro. Use o Protocolo de Transporte de Hipertexto Seguro (HTTPS) para se comunicar com êxito com o servidor da API RESTful.

O servidor da API RESTful não considera a segurança de transporte (como SSL), mas, em vez disso, assume que as solicitações são iniciadas a partir de um servidor local, seguro. O protocolo HTTPS fornece privacidade por meio da criptografia de dados. A API RESTful fornece mais segurança, requerendo autenticação de comando, que persiste por duas horas de atividade, ou 30 minutos de inatividade, o que ocorrer primeiro.

Os Localizadores Uniformes de Recursos (URLs) direcionam objetos de nó diferentes no sistema. O método HTTPS POST age em destinos de comandos que são especificados na URL. Para obter mais informações, consulte Destinos e Características do Comando da API RESTful. Para fazer mudanças ou visualizar informações sobre objetos diferentes no sistema, você deve criar e enviar uma solicitação ao sistema. Você é solicitado a fornecer determinados elementos para o servidor da API RESTful para receber e transformar a solicitação em um comando, conforme descrito na próxima seção.

Fazendo uma solicitação de HTTPS

Para interagir com o sistema usando a API RESTful, faça uma solicitação de comando HTTPS com um destino de URL de nó de configuração válido. Abra a porta TCP 7443 e inclua a palavra-chave rest. Para IBM Spectrum Virtualize for Public Cloud no Amazon Web Services (AWS), deve-se abrir manualmente a porta TCP 7443 no grupo de segurança AWS antes de usar a API RESTful. Use o formato de URL a seguir para todas as solicitações:

https://system_node_ip: 7443/rest/command
Em que:
  • system_node_ip é o endereço IP do sistema, que é o endereço que é obtido pelo nó de configuração do sistema.
  • O número da porta é sempre 7443 para a API RESTful do Spectrum Virtualize.
  • rest é uma palavra-chave.
  • command é o objeto de comando de destino (tal como auth ou lseventlog com quaisquer parâmetros).
    A especificação command segue este formato:
    command_name,method="POST",headers={'parameter_name': 'parameter_value',
    ' parameter_name ': ' parameter_value ', ...}
Por que usamos o POST exclusivamente: Todos os destinos de comandos da API RESTful Spectrum Virtualize são nomeados após comandos do Spectrum Virtualize, com nomes que já refletem as ações Create, Read, Update e Delete. Os comandos MK fazem recursos (criar), os comandos LS listam recursos (ler), os comandos CH alteram recursos (atualizar) e os comandos RM removem recursos (excluir). O uso de métodos HTTP sobre esses comandos é redundante.

Todos os comandos, incluindo comandos LS, aceitam pelo menos um parâmetro nomeado. Como a API RESTful do Spectrum Virtualize é apenas o método POST, ela implementa uma convenção de anexação de parâmetros posicionais ao URI e o empacotamento de parâmetros nomeados no corpo da solicitação como sequências JavaScript Object Notation (JSON).

Por que não usar GET: A maioria dos servidores HTTP rejeita solicitações GET com dados do corpo. Para o suporte, GET significaria anexar parâmetros nomeados ao URI como uma sequência de consultas. Não inclua tais dados arbitrários em uma sequência de consultas. Um URI válido não pode conter espaço em branco e outros caracteres reservados que possam estar presentes nos dados. Portanto, você deve codificar por URL os dados do parâmetro nomeados antes de anexá-los ao URI. Além dessa inconveniência, a estrutura de chave-valor de dados do parâmetro nomeados deve ser transmitida de alguma forma. A sequência de consultas provavelmente seria um objeto JSON codificado por URL. O suporte do método GET significa que os dados do parâmetro representados em sua linguagem de programação escolhida devem passar por dois codificadores separados antes de os dados serem enviados como parte de uma solicitação de HTTP.

O método mais elegante para os destinos de comandos era adotar o POST exclusivamente.

Conforme mencionado, além da URL e do nome do destino do comando, outras informações são necessárias na linha de solicitação e no corpo da solicitação de HTTP que consideram a ação a ser tomada no objeto especificado. Na linha de solicitação, inclua o método HTTP POST. Inclua quaisquer parâmetros necessários (como nível do RAID ou endereço IP) no corpo da solicitação.

Forneça todos os parâmetros necessários como JSON válido no corpo HTTP, como mostrado no exemplo a seguir:
{'X-Auth-Username': 'superuser'}
A solicitação é roteada para a porta 7443 no destino especificado (que deve ser o nó de configuração do sistema), em que a solicitação é recebida pelo servidor de API RESTful. O servidor executa o comando, coleta qualquer saída resultante e, em seguida, cria uma resposta HTTP, como o exemplo a seguir:
HTTP/1.1 200 OK
Servidor: lighttpd / 1.4.31
Data: data
Content-type: application/json; charset = UTF-8 
Content-length: content_length
Conexão: fechada
{"attribute": "value"}

Para visualizar os destinos de comando da API e suas características, consulte Destinos e Características do Comando da API RESTful. Para ver um exemplo de como iniciar, consulte Iniciando. Consulte Mensagens de Erro HTTP da API RESTful para obter uma lista completa de códigos de erro HTTP que você pode encontrar.

Destinos e Características do Comando da API RESTful

O Tabela 1 enfatiza o método POST para todos os comandos, incluindo /auth. Também mostra que você deve usar o token de autenticação que é retornado pelo destino de comando /auth para autenticar todos os outros comandos executados. Exceto para o destino de comando /auth, você executa comandos no endereço IP do sistema para que eles sejam executados pelo nó de configuração.

Tabela 1. Método POST, requisitos de autenticação e se deve ser executado no nó de configuração
Destinos de Comando Método Autenticação necessária Executar no Nó de Configuração / Cluster
/auth POST Não Não
Todos os outros destinos de comando POST Sim Sim
Tabela 2 mostra os nomes de destino de comando dos comandos mais usados para esta liberação da API RESTful. Seguindo a convenção, os comandos executáveis svcinfo e svctask são padrões e não requerem listagem em destinos de comandos da API RESTful.

As descrições dos destinos de comandos e seus parâmetros, e as descrições de outros comandos usados com menos frequência estão disponíveis na seção de comandos da CLI da documentação do produto.

Tabela 2. Comandos da API RESTful suportados para o software Spectrum Virtualize
Destinos de Comando
/addhostclustermember /addhostiogrp /addhostport
/addvdiskaccess /addvdiskcopy /addvolumecopy
/auth /chhost /chnode
/chnodecanister /chrcconsistgrp /chrcrelationship
/chvdisk /expandvdisksize /lscurrentuser
/lseventlog /lsfcconsistgrp /lsfcmap
/lsfcmapcandidate /lsfcmapdependentmaps /lsfcmapprogress
/lshost /lshostcluster /lshostclustermember
/lshostclustervolumemap /lshostiogrp /lshostvdiskmap
/lsiogrp /lsiogrphost /lsmdiskgrp
/lsnode /lsnodecanister /lsnodehw
/lsnodecanisterhw /lsnodecanisterstats /lsnodecanistervpd
/lsnodehw /lsnodestats /lsnodevpd
/lspartnership /lsrcrelationshipprogress /lssystem
/lssystemip /lssystemstats /lsvdisk
/lsvdiskaccess /lsvdiskcopy /lsvdiskfcmapcopies
/lsvdiskfcmappings /lsvdiskhostmap /lsvdisksyncprogress
/mkfcconsistgrp /mkfcmap /mkfcpartnership
/mkhost /mkhostcluster /mkrcconsistgrp
/mkrcrelationship /mkvdisk /mkvdiskhostmap
/mkvolume /mkvolumehostclustermap /movevdisk
/prestartfcconsistgrp /prestartfcmap /rmfcmap
/rmhost /rmhostcluster /rmhostclustermember
/rmhostiogrp /rmhostport /rmvdisk
/rmvdiskaccess /rmvdiskcopy /rmvdiskhostmap
/rmvolume /rmvolumecopy /rmvolumehostclustermap
/startfcmap /startrcconsistgrp /startrcrelationship
/stopfcconsistgrp /stopfcmap /stoprcconsistgrp
/stoprcrelationship    

Visão geral de autenticação

Além da criptografia de dados, o servidor HTTPS requer autenticação de um nome de usuário e senha válidos para cada sessão da API. Use dois campos de cabeçalho de autenticação para especificar suas credenciais: X-Auth-Username e X-Auth-Password.

A autenticação inicial requer que você execute POST no destino de autenticação (/auth) com o nome do usuário e a senha. O servidor da API RESTful retorna um token hexadecimal. Uma sessão única dura no máximo duas horas ativas ou trinta minutos inativos, o que ocorrer primeiro. Quando sua sessão for encerrada por inatividade, ou se você atingir o tempo máximo alocado, o código de erro 403 indica a perda de autorização. Use o destino de comando /auth para autenticar-se novamente com o nome do usuário e a senha.

Por exemplo, o comando a seguir passa o comando de autenticação para o IP do nó de destino 192.168.10.109 na porta 7443:

https://192.168.10.109:7443/rest/auth, method="POST ", 
   headers = {0}rd': 'passw0rd'
A solicitação de HTTP que foi enviada para o servidor da API é semelhante a:
POST /auth HTTPS/HTTPS_version 
Host: https://192.168.10.109:7443
Content-type: application/json; charset = UTF-8 
Content-length: message_size
X-Auth-Token: 58cfd6acb1676e1cba78b7cb5a9a081d11d1d1d1cfeb0078083ef225d9c59bf4df

{"attribute": "value","attribute": "value"}
Em que:
  • A primeira linha é a linha de solicitação com o método POST, o destino da API, o protocolo (HTTPS) e a versão do protocolo (1.1).
  • A segunda linha é o cabeçalho do host, direcionando a solicitação de HTTP para a porta correta (7443) e o endereço IP no sistema.
  • A terceira linha é o cabeçalho do tipo de conteúdo que especifica o tipo de conteúdo (application/json; charset=UTF-8).
  • A quarta linha é o cabeçalho de comprimento de conteúdo com o tamanho da mensagem.
  • A quinta linha é o cabeçalho do token de autenticação com o token de autenticação.
  • Um espaço é deixado entre os cabeçalhos e o corpo da solicitação. Quaisquer parâmetros aparecem em JSON na sétima linha.
O comando auth bem-sucedido retorna um token semelhante ao exemplo a seguir:
{
"token": "58cfd6acb1676e1cba78b7cb5a9a081d11d1d1d1cfeb0078083ef225d9c59bf4df"
}

Parâmetros de destino do comando

Os destinos de comando interagem com partes diferentes do sistema. Depois de direcionar um nó específico no sistema (geralmente um nó de configuração), direcione um objeto nesse nó. Consulte Tabela 2 para obter destinos de comandos e consulte a seção de comandos da CLI dessa documentação do produto para obter descrições de comandos individuais de parâmetros que podem ser especificados.

Iniciando

O seguinte exemplo Python 3 mostra como concluir a configuração inicial para começar a interagir com o sistema e executar comandos. Para obter exemplos em outros idiomas, consulte Exemplos de uso da API RESTful em Perl e Exemplos de uso em CURL.

import ssl
import json
importar pprint

import urllib.request
import urllib.error
import urllib.parse

no_verify = ssl.create_default_context ()
no_verify.check_hostname = False
no_verify.verify_mode = ssl.CERT_NONE

if getattr (ssl, '_https_verify_certificates', None):
    ssl. _https_verify_certificates (False)

class HostString (str):
    """
        Comentário: subclasse especial de sequência para armazenar atributos relacionados ao host arbitrário
                    (como auth tokens) sem perder qualquer comportamento da sequência
    """
    def __new__ (cls, * args, ** kwds):
        return super (HostString, cls). __new__ (cls, * args, ** kwds)

class RESTUtil(object):
    show_default=False
    default_headers = {}
    port = 80

    def __init__(self, show=None, catch=True):
        self.hosts = {}
        self.curr_host = None
        self.catch=catch
        self.show_default=show if show != None else self.show_default

    @property
    def host(self):
        return self.curr_host

    @host.setter
    def host(self, hostname):
        """
            Comentário: recupere o objeto HostString de um host conhecido a partir de seu
                     nome do host ou definição de sequência. Mesmo se a definição do host 
                     for fornecida, ainda será necessário chavear para self.hosts no caso de 
                     classes do cliente estarem armazenando coisas em seus objetos HostString.
        """
        try:
            if hostname in self.hosts:
                self.curr_host = self.hosts[hostname]
            else:
                self.curr_host = [h for h in self.hosts.values() if h == hostname][0]
            return self.curr_host
        except IndexError:
            raise KeyError("Unrecognized host/name %s" % hostname)

    def add_host(self, hostdef, hostname=None):
        hostname = hostname if hostname is None else hostdef
        self.hosts[hostname] = HostString(hostdef)
        if self.curr_host == None:
            self.curr_host = self.hosts[hostname]
        return hostname

    def command(self, protocol, postfix, method='POST', headers=None, show=None, **cmd_kwds):
        """
            Comentário: um construtor de solicitação de API RESTful razoavelmente genérico. 
                     Consulte as subclasses para obter exemplos de uso.
        """
        if show == None:
            show = self.show_default
        headers = {} if headers == None else headers
        url = '%s://%s:%s/%s' % (
            protocol,
            self.curr_host,
            self.port,
            postfix
        )
        request = urllib.request.Request(
            url,
            headers =dict(self.default_headers, **headers),
            data=bytes(json.dumps(cmd_kwds), encoding="utf-8") if cmd_kwds else None)
        request.get_method = lambda: method
        if show:
            self.request_pprint(request)
        try:
            cmd_out = urllib.request.urlopen(request, context=no_verify).read().decode('utf-8')
        except urllib.error.HTTPError as e:
            self.exception_pprint(e)
            if not self.catch:
                raise Exception("RESTful API command failed.")
            return
        try:
            cmd_out = json.loads(cmd_out)
        except ValueError:
            pass
        if show:
            print("\nCommand Output:")
            pprint.pprint(cmd_out)
            print("")
        return cmd_out

    @staticmethod
    def request_pprint(request):
        """
            Comentário: Função de impressão de informações de pedido 
                     (para auto.comando com show = True)
        """
        print(request.get_method(), request.get_full_url(), 'HTTP/1.1')
        print('Host:', request.host)
        for key, value in request.headers.items():
            print(key.upper() + ':', str(value))
        if request.data != None:
            print()
            pprint.pprint(request.data)

    @staticmethod
    def exception_pprint(http_error):
        """
            Comentário: função de impressão de informações HTTPError
        """
        print (http_error.code, '-- --', http_error.reason)
        print (http_error.fp.read ())
        print("")

classe SVCREST (RESTUtil):
    """
        Comentário: wrapper RESTful para a CLI SVC
    """

    def __init__ (self, host, * args, ** kwds):
        self.debug = kwds.pop( 'debug', False)
        super().__init__(*args, **kwds)
        self.add_host (host)

    @property
    def default_headers (self):
        return {'X-Auth-Token': getattr(self.curr_host, 'token', 'badtoken'),
                'Content-Type': 'application/json' }

    @property
    def port (self):
        return getattr(self, '_port', None) or ('7665' if self.debug else '7443')

    @property
    protocolo def (self):
        return getattr(self, '_protocol', None) or ('http' if self.debug else 'https')

    def command(self, cmd, *args, method="POST", headers=None, show=None, **cmd_kwds):
        postfix = '/' .join (
            ['rest'] + [cmd] + [urllib.parse.quote(str(a)) for a in args]
        )
        return super( ).comando (
            self.protocol,
            postfix,
            method=method,
            headers=headers,
            show=show,
            ** cmd_kwds
        )

    def authenticate (self, username = 'superuser', password= 'passw0rd', show = None):
        cmd_out = self.command (
            'auth', show=show, method="POST", headers={'X-Auth-Username': username, 'X-Auth-Password': password}
        )
        if cmd_out:
            self.curr_host.token = cmd_out [ 'token' ]
    """
       Comentário: primeiro, configure o ipaddress do cluster.  
          Supõe-se que superuser/passw0rd (6 linhas acima) seja a credencial.
          Após a chamada autenticada, é possível emitir qualquer comando em 
                s.command('') that is an svcinfo or svctask cmmand)
   """
s = SVCREST( '192.168.10.109')
s.authenticate ()
print (s.command ('lssystem'))