Skip to content
Draft
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 @@ -5,10 +5,6 @@
"flavor": "rpi4b",
"description": "Raspberry Pi 4",
"model": "rpi4b",
"peripherals": false,
"quotas": {
"cores": 4,
"cpus": 4
}
"peripherals": false
}
]
Original file line number Diff line number Diff line change
@@ -1,134 +1,90 @@
from typing import Optional

import requests

from .exceptions import CorelliumApiException
from .types import Device, Instance, Project, Session
import corellium_api
from corellium_api import Instance, Model, Project


class ApiClient:
"""
Corellium ReST API client used by the Corellium driver.
"""
session: Session
req: requests.Session

def __init__(self, host: str, token: str) -> None:
"""
Initializes a new client, containing a
"""
self.host = host
self.token = token
self.session = None
self.req = requests.Session()

configuration = corellium_api.Configuration(host=self.baseurl, disabled_client_side_validations="multipleOf")
configuration.access_token = token
configuration.client_side_validation = False
corellium_api.Configuration.set_default(configuration)
self.api = corellium_api.CorelliumApi(corellium_api.ApiClient(configuration))

@property
def baseurl(self) -> str:
"""
Return the baseurl path for API calls.
"""
return f'https://{self.host}/api'

def login(self) -> None:
"""
Login against Corellium's ReST API.

Set an internal Session object instance to be used
in other API calls that require authentication.

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

try:
res = requests.post(f'{self.baseurl}/v1/auth/login', json=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

self.session = Session(**data)
self.req.headers.update(self.session.as_header())
return f"https://{self.host}/api"

def get_project(self, project_ref: str = 'Default Project') -> Optional[Project]:
async 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.
"""
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

for project in data:
if project['name'] == project_ref or project['id'] == project_ref:
return Project(id=project['id'], name=project['name'])
projects = await self.api.v1_get_projects()
for project in projects:
if project.name == project_ref or project.id == project_ref:
return project

return None

def get_device(self, model: str) -> Optional[Device]:
async def get_device(self, model: str) -> Optional[Model]:
"""
Get a device spec from Corellium's list based on the model name.

A device object is used to create a new virtual instance.
"""
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

for device in data:
if device['model'] == model:
return Device(**device)
models = await self.api.v1_get_models()
for device in models:
if device.model == model:
return device

return None

def create_instance(self, name: str, project: Project, device: Device, os_version: str, os_build: str) -> Instance:
async def create_instance(
self, name: str, project: Project, device: Model, os_version: str, os_build: str
) -> Instance:
"""
Create a new virtual instance from a device spec.
"""
data = {
'name': name,
'project': project.id,
'flavor': device.flavor,
'os': os_version,
'osbuild': os_build,
}

try:
res = self.req.post(f'{self.baseurl}/v1/instances', json=data)
data = res.json()
res.raise_for_status()
except requests.exceptions.RequestException as e:
raise CorelliumApiException(data.get('error', str(e))) from e
return await self.api.v1_create_instance(
corellium_api.InstanceCreateOptions(
name=name,
project=project.id,
flavor=device.flavor,
os=os_version,
osbuild=os_build,
)
)

return Instance(**data)

def get_instance(self, instance_ref: str) -> Optional[Instance]:
async def get_instance(self, instance_ref: str) -> Optional[Instance]:
"""
Retrieve an existing instance by its name.

Return None if it does not exist.
"""
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

for instance in data:
if instance['name'] == instance_ref or instance['id'] == instance_ref:
return Instance(id=instance['id'], state=instance['state'])
instances = await self.api.v1_get_instances()
for instance in instances:
if instance.name == instance_ref or instance.id == instance_ref:
return instance

return None

def set_instance_state(self, instance: Instance, instance_state: str) -> None:
async def set_instance_state(self, instance: Instance, instance_state: str) -> None:
"""
Set the virtual instance state from corellium.

Expand All @@ -144,30 +100,17 @@ def set_instance_state(self, instance: Instance, instance_state: str) -> None:
- rebooting
- error
"""
data = {
'state': instance_state
}

try:
res = self.req.put(f'{self.baseurl}/v1/instances/{instance.id}/state', json=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)

raise CorelliumApiException(msgerr) from e
await self.api.v1_set_instance_state(
instance.id,
corellium_api.V1SetStateBody(state=instance_state),
)

def destroy_instance(self, instance: Instance) -> None:
async def destroy_instance(self, instance: Instance) -> None:
"""
Delete a virtual instance.

Does not return anything since Corellium's API return a HTTP 204 response.
"""
try:
res = self.req.delete(f'{self.baseurl}/v1/instances/{instance.id}')
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)

raise CorelliumApiException(msgerr) from e
await self.api.v1_delete_instance(instance.id)
Loading
Loading