Panoptica REST API

Intro

The Cisco Panoptica REST API has endpoints to perform all activities that can be performed on the console UI, including endpoints to create environments and clusters, create policies, add images and registries.

The REST API uses authentication described below, which uses a Python library provided by Cisco.

Pre-requisites

  • Python, version 3
  • Python libraries (pip) - decorator (4.3.0), requests (2.18.4), warrant (0.6.1)
  • Cisco Panoptica authentication library, escherauth.py from here

Base URL

The base URL for the REST API is - https://securecn.cisco.com/api

API Reference

Click API on the Panoptica home page to open the API reference, in Swagger. This shows details of each endpoint and method.

879

Authentication

The REST API uses escher authentication. This method uses a unique token for each request. The token is a hash generated from fixed API and Secret keys, obtained from Panoptica, the request URL, and the request time.

API & Secret keys

Generate unique API and Secret keys from the Panoptica console. These will be used for each request.

  1. Navigate to the System page, and then select MANAGE USERS.
  2. Click New User and then select Service User.
  3. Enter a name for the user. Leave the status as 'Active'.
  4. Click FINISH. Copy the values of Access Key and Secret Key, as they will be used later.
583

Escher Auth Key

Use the attached Python files to generate the Escher authentication.
It uses these parameters:

  • the API & Secret keys (above)
  • the current date & time, in format ...
  • the request URL, in format ...

Use these in the following way (for Python)

response = requests.get(full_url,
                          headers={'X-Escher-Date': date_string,
                                   'host': 'securecn.cisco.com',
                                   'content-type': 'application/json'},
                          auth=EscherRequestsAuth("global/services/portshift_request",
                                                  {'current_time': date},
                                                  {'api_key': access_key, 'api_secret': secret_key}))

In the snippet above:
global/services/portshift_request - is the API request URL
current_date - is the current date, formed in the following way:

date_format = '%Y%m%dT%H%M%SZ'
  date_string = datetime.datetime.utcnow().strftime(date_format)
  date = datetime.datetime.strptime(date_string, date_format)
  • api_key and api_secret *are as obtained above, for the Service User.

Escher authentication (python)

Download the authentication module (Python) from here.

Use example (Python)

The example below generates a request for the Get Image Layers endpoint.

# coding=utf-8
import datetime
import json
import decorator
import os
import sys
import traceback
import requests
from escherauth import EscherRequestsAuth


def main():
  access_key = "the 'access key' from a 'SERVICE USER' in https://securecn.cisco.com/system/manageUsers"
  secret_key = "the 'secret key' from a 'SERVICE USER' in https://securecn.cisco.com/system/manageUsers"

  image_id = "some id" # you need to take the id of the image
  response = get_image_layers(
    access_key,
    secret_key,
    "https://securecn.cisco.com",
    image_id)

  print(response)


@verify
def get_image_layers(access_key, secret_key, url, image_id):
  full_url = url + '/api/api/images/' + image_id + '/imageLayers'

  print("full_url = " + full_url)

  if not access_key:
    access_key = os.environ['PORTSHIFT_ACCESS_KEY']

  if not secret_key:
    secret_key = os.environ['PORTSHIFT_SECRET_KEY']

  date_format = '%Y%m%dT%H%M%SZ'
  date_string = datetime.datetime.utcnow().strftime(date_format)
  date = datetime.datetime.strptime(date_string, date_format)
  response = requests.get(full_url,
                          headers={'X-Escher-Date': date_string,
                                   'host': 'securecn.cisco.com',
                                   'content-type': 'application/json'},
                          auth=EscherRequestsAuth("global/services/portshift_request",
                                                  {'current_time': date},
                                                  {'api_key': access_key, 'api_secret': secret_key}))

  print("response.status_code = " + str(response.status_code))

  if response.status_code not in [200]:
    raise Exception("Failed to get image layers for image " + image_id + "." + str(response))


  return response




@verify
def add_kubernetes_cluster(name, access_key, secret_key, url, cluster_pod_definition_source='KUBERNETES',
                           ci_validation='false', should_auth_fail=False):
  full_url = url + '/api/api/kubernetesClusters'

  print("full_url = " + full_url)
  print("should_auth_fail = " + str(should_auth_fail))

  if not access_key:
    access_key = os.environ['PORTSHIFT_ACCESS_KEY']

  if not secret_key:
    secret_key = os.environ['PORTSHIFT_SECRET_KEY']

  date_format = '%Y%m%dT%H%M%SZ'
  date_string = datetime.datetime.utcnow().strftime(date_format)
  date = datetime.datetime.strptime(date_string, date_format)
  cluster_dict = create_kubernetes_cluster_dict(name, cluster_pod_definition_source, ci_validation)
  cluster_json = json.dumps(cluster_dict)
  response = requests.post(full_url,
                           data=cluster_json,
                           headers={'X-Escher-Date': date_string,
                                    'host': 'securecn.cisco.com',
                                    'content-type': 'application/json'},
                           auth=EscherRequestsAuth("global/services/portshift_request",
                                                   {'current_time': date},
                                                   {'api_key': access_key, 'api_secret': secret_key}))


  print("response.status_code = " + str(response.status_code))


  if response.status_code not in [201, 409]:
    raise Exception("Failed to create cluster. " + str(response))

  return response


@decorator.decorator
def verify(func, *args, **kwargs):
  res = func(*args, **kwargs)

  if res.ok is False:
    try:
      raise Exception(res.json())
    except ValueError:
      raise Exception("Error code was {} but there was no bodjson. Text was '{}'".format(res.status_code, res.text))

  if res.status_code != 204:
    try:
      return res.json()
    except ValueError:
      raise Exception("Status code was {} but there was no json. Text was '{}'".format(
        res.status_code, res.text))
  return 'OK'


def create_kubernetes_cluster_dict(name, clusterPodDefinitionSource, ci_validation):
  cluster_dict = \
    {
      "name": name,
      "clusterPodDefinitionSource": clusterPodDefinitionSource,
      "ciImageValidation": ci_validation,
      "enableConnectionsControl": True
    }
  return cluster_dict


if __name__ == "__main__":
  try:
    main()
  except Exception as exc:
    traceback.print_exc()
    sys.exit(1)