Skip to content
This repository was archived by the owner on Jan 23, 2026. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,4 @@ export:
# optional
device_os: "1.1.1"
device_build: "Critical Application Monitor (Baremetal)"
console_name: "Primary Compute Non-Secure"
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
{
"id": "7f4f241c-821f-4219-905f-c3b50b0db5dd",
"name": "my-instance",
"key": "key",
"flavor": "kronos",
"flavorName": "RD-1AE",
"type": "iot",
"project": "778f00af-5e9b-40e6-8e7f-c4f14b632e9c",
"domainName": "my-domain",
"state": "on",
"stateChanged": "9999-12-31T00:00:00.000Z",
"startedAt": "9999-12-31T00:00:00.000Z",
"userTask": null,
"taskState": "none",
"restoreStatus": {},
"error": null,
"bootOptions": {
"bootArgs": "",
"uuid": "uuid",
"ecid": "ecid",
"noSnapshotMount": true
},
"serviceIp": "0.0.0.0",
"wifiIp": "0.0.0.0",
"wifiMac": "mac",
"secondaryIp": null,
"port-gdb": "1234",
"port-usbmuxd": "27015",
"port-1-cons": "4001",
"port-2-cons": "4002",
"consoles": [
{
"name": "Console 1",
"id": "port-1-cons",
"port": "4001",
"hash": "post-1-cons-hash",
"info": "port-1-cons-info"
},
{
"name": "Console 2",
"id": "port-2-cons",
"port": "4002",
"hash": "port-2-cons-hash",
"info": "port-2-cons-info"
}
],
"services": {
"vpn": {
"proxy": [],
"listeners": []
}
},
"panicked": false,
"created": "9999-12-31T00:00:00.000Z",
"os": "1.1",
"agent": null,
"netmon": {
"hash": "netmon-hash",
"info": "netmon-info",
"enabled": false
},
"netdump": {
"hash": "netdump-hash",
"info": "netdump-info",
"enabled": false
},
"coreTrace": {
"enabled": null
},
"hyperTrace": {
"enabled": null
},
"exposePort": null,
"fault": null,
"patches": [
"jailbroken"
],
"createdBy": {
"id": "2eadc0a4-bd23-44dd-9a2c-0aef784d0c43",
"username": "some@user.email",
"label": "full name"
},
"mast": false
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"url": "wss://api-host/port-cons-1"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"error":"not a valid type",
"errorID":"UserError",
"field":"type"
}
Original file line number Diff line number Diff line change
Expand Up @@ -38,16 +38,19 @@ def login(self) -> None:

It uses the global requests objects so a new session can be generated.
"""
data = {
data = None
req_data = {
'apiToken': self.token
}

try:
res = requests.post(f'{self.baseurl}/v1/auth/login', json=data)
res = requests.post(f'{self.baseurl}/v1/auth/login', json=req_data)
data = res.json()
res.raise_for_status()
except (requests.exceptions.RequestException, requests.exceptions.HTTPError) as e:
raise CorelliumApiException(data.get('error', str(e))) from e
msgerr = data.get('error') if data is not None else str(e)

raise CorelliumApiException(msgerr) from e

self.session = Session(**data)
self.req.headers.update(self.session.as_header())
Expand All @@ -56,12 +59,16 @@ def get_project(self, project_ref: str = 'Default Project') -> Optional[Project]
"""
Retrieve a project based on project_ref, which is either its id or name.
"""
data = None

try:
res = self.req.get(f'{self.baseurl}/v1/projects')
data = res.json()
res.raise_for_status()
except requests.exceptions.RequestException as e:
raise CorelliumApiException(data.get('error', str(e))) from e
msgerr = data.get('error') if data is not None else str(e)

raise CorelliumApiException(msgerr) from e

for project in data:
if project['name'] == project_ref or project['id'] == project_ref:
Expand All @@ -75,12 +82,16 @@ def get_device(self, model: str) -> Optional[Device]:

A device object is used to create a new virtual instance.
"""
data = None

try:
res = self.req.get(f'{self.baseurl}/v1/models')
data = res.json()
res.raise_for_status()
except requests.exceptions.RequestException as e:
raise CorelliumApiException(data.get('error', str(e))) from e
msgerr = data.get('error') if data is not None else str(e)

raise CorelliumApiException(msgerr) from e

for device in data:
if device['model'] == model:
Expand All @@ -92,7 +103,8 @@ def create_instance(self, name: str, project: Project, device: Device, os_versio
"""
Create a new virtual instance from a device spec.
"""
data = {
data = None
req_data = {
'name': name,
'project': project.id,
'flavor': device.flavor,
Expand All @@ -101,11 +113,13 @@ def create_instance(self, name: str, project: Project, device: Device, os_versio
}

try:
res = self.req.post(f'{self.baseurl}/v1/instances', json=data)
res = self.req.post(f'{self.baseurl}/v1/instances', json=req_data)
data = res.json()
res.raise_for_status()
except requests.exceptions.RequestException as e:
raise CorelliumApiException(data.get('error', str(e))) from e
msgerr = data.get('error') if data is not None else str(e)

raise CorelliumApiException(msgerr) from e

return Instance(**data)

Expand All @@ -115,12 +129,16 @@ def get_instance(self, instance_ref: str) -> Optional[Instance]:

Return None if it does not exist.
"""
data = None

try:
res = self.req.get(f'{self.baseurl}/v1/instances')
data = res.json()
res.raise_for_status()
except requests.exceptions.RequestException as e:
raise CorelliumApiException(data.get('error', str(e))) from e
msgerr = data.get('error') if data is not None else str(e)

raise CorelliumApiException(msgerr) from e

for instance in data:
if instance['name'] == instance_ref or instance['id'] == instance_ref:
Expand All @@ -144,16 +162,17 @@ def set_instance_state(self, instance: Instance, instance_state: str) -> None:
- rebooting
- error
"""
data = {
data = None
req_data = {
'state': instance_state
}

try:
res = self.req.put(f'{self.baseurl}/v1/instances/{instance.id}/state', json=data)
res = self.req.put(f'{self.baseurl}/v1/instances/{instance.id}/state', json=req_data)
data = res.json() if res.status_code != 204 else None
res.raise_for_status()
except requests.exceptions.RequestException as e:
msgerr = data if data is not None else str(e)
msgerr = data.get('error') if data is not None else str(e)

raise CorelliumApiException(msgerr) from e

Expand All @@ -168,6 +187,47 @@ def destroy_instance(self, instance: Instance) -> None:
data = res.json() if res.status_code != 204 else None
res.raise_for_status()
except requests.exceptions.RequestException as e:
msgerr = data if data is not None else str(e)
msgerr = data.get('error') if data is not None else str(e)

raise CorelliumApiException(msgerr) from e

def get_instance_console_id(self, instance: Instance, console_name: str) -> Optional[str]:
"""
Retrieve an instance's console id by its name.

Return None if it does not exist.
"""
data = None

try:
res = self.req.get(f'{self.baseurl}/v1/instances/{instance.id}')
data = res.json()
res.raise_for_status()
except requests.exceptions.RequestException as e:
msgerr = data.get('error') if data is not None else str(e)

raise CorelliumApiException(msgerr) from e

for console in data.get('consoles', []):
if console['name'] == console_name:
return console['id']

return None

def get_instance_console_url(self, instance: Instance, console_id: str) -> Optional[str]:
"""
Get a a console URL (websocket) to stream logs from.
"""
data = None

try:
res = self.req.get(f'{self.baseurl}/v1/instances/{instance.id}/console',
params={'type': console_id.replace('port-', '')})
data = res.json()
res.raise_for_status()
except requests.exceptions.RequestException as e:
msgerr = data.get('error') if data is not None else str(e)

raise CorelliumApiException(msgerr) from e

return data['url']
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import pytest

# import websockets
from .api import ApiClient
from .exceptions import CorelliumApiException
from .types import Device, Instance, Project, Session
Expand Down Expand Up @@ -167,7 +168,7 @@ def test_destroy_instance_state_ok(requests_mock):
'status_code,data,msg',
[
(403, fixture('http/403.json'), 'Invalid or missing authorization token'),
(404, fixture('http/get-instance-state-404.json'), 'No instance associated with this value'),
(404, fixture('http/get-instance-404.json'), 'No instance associated with this value'),
])
def test_destroy_instance_error(requests_mock, status_code, data, msg):
instance = Instance(id='d59db33d-27bd-4b22-878d-49e4758a648e')
Expand All @@ -180,3 +181,77 @@ def test_destroy_instance_error(requests_mock, status_code, data, msg):
api.destroy_instance(instance)

assert msg in str(e.value)


@pytest.mark.parametrize(
'console_name,console_id',
[
('Console 1', 'port-1-cons',),
('Console 10', None,),
('Console 2', 'port-2-cons',),
])
def test_get_instance_console_id_ok(requests_mock, console_name, console_id):
data = fixture('http/get-instance-200.json')
instance = Instance(id='d59db33d-27bd-4b22-878d-49e4758a648e')
requests_mock.get(f'https://api-host/api/v1/instances/{instance.id}', status_code=200, text=data)
api = ApiClient('api-host', 'api-token')
api.session = Session('session-token', '2022-03-20T01:50:10.000Z')

current = api.get_instance_console_id(instance, console_name)

assert console_id == current


@pytest.mark.parametrize(
'status_code,data,msg',
[
(403, fixture('http/403.json'), 'Invalid or missing authorization token'),
(404, fixture('http/get-instance-404.json'), 'No instance associated with this value'),
])
def test_get_instance_console_id_error(requests_mock, status_code, data, msg):
instance = Instance(id='d59db33d-27bd-4b22-878d-49e4758a648e')
requests_mock.get(f'https://api-host/api/v1/instances/{instance.id}',
status_code=status_code, text=data)
api = ApiClient('api-host', 'api-token')
api.session = Session('session-token', '2022-03-20T01:50:10.000Z')

with pytest.raises(CorelliumApiException) as e:
api.get_instance_console_id(instance, 'Console 1')

assert msg in str(e.value)


def test_get_instance_console_url_ok(requests_mock):
console_id = 'port-1-cons'
data = fixture('http/get-instance-console-url-200.json')
instance = Instance(id='d59db33d-27bd-4b22-878d-49e4758a648e')
requests_mock.get(f'https://api-host/api/v1/instances/{instance.id}/console?type=1-cons',
status_code=200, text=data)
api = ApiClient('api-host', 'api-token')
api.session = Session('session-token', '2022-03-20T01:50:10.000Z')

current = api.get_instance_console_url(instance, console_id)
expected = 'wss://api-host/port-cons-1'

assert expected == current


@pytest.mark.parametrize(
'status_code,data,msg',
[
(400, fixture('http/get-instance-console-url-400.json'), 'not a valid type'),
(403, fixture('http/403.json'), 'Invalid or missing authorization token'),
(404, fixture('http/get-instance-404.json'), 'No instance associated with this value'),
])
def test_get_instance_console_url_error(requests_mock, status_code, data, msg):
console_id = 'port-1-cons'
instance = Instance(id='d59db33d-27bd-4b22-878d-49e4758a648e')
requests_mock.get(f'https://api-host/api/v1/instances/{instance.id}/console?type=1-cons',
status_code=status_code, text=data)
api = ApiClient('api-host', 'api-token')
api.session = Session('session-token', '2022-03-20T01:50:10.000Z')

with pytest.raises(CorelliumApiException) as e:
api.get_instance_console_url(instance, console_id)

assert msg in str(e.value)
Loading
Loading