Skip to content

Commit

Permalink
Saimon/migration tool (#174)
Browse files Browse the repository at this point in the history
  • Loading branch information
saimonation committed Feb 23, 2023
1 parent f638816 commit df1f05a
Show file tree
Hide file tree
Showing 17 changed files with 835 additions and 25 deletions.
8 changes: 5 additions & 3 deletions .pylintrc
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,9 @@ disable=raw-checker-failed,
duplicate-code,
no-member,
unsubscriptable-object,
raise-missing-from
raise-missing-from,
use-dict-literal,
broad-exception-raised


# Enable the message, report, category or checker with the given id(s). You can
Expand Down Expand Up @@ -507,5 +509,5 @@ known-third-party=enchant

# Exceptions that will emit a warning when being caught. Defaults to
# "BaseException, Exception".
overgeneral-exceptions=BaseException,
Exception
overgeneral-exceptions=builtins.BaseException,
builtins.Exception
87 changes: 72 additions & 15 deletions cterasdk/client/cteraclient.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
from .http import HTTPClient, ContentType, HTTPException, HTTPResponse, geturi
from ..convert import fromxmlstr, toxmlstr
from ..convert import fromxmlstr, fromjsonstr, toxmlstr, tojsonstr
from ..exception import CTERAClientException
from ..lib import Command
from ..common import Object
Expand Down Expand Up @@ -88,24 +88,81 @@ def set_authorization_headers(self, headers):

@staticmethod
def fromxmlstr(request, response):
if not config.transcript['disabled']:
response = HTTPResponse(response)
transcribe.transcribe(request, response)
return fromxmlstr(response.content.decode('utf-8'))
response = transcribe_request(request, response, True)
return fromxmlstr(response.text)

@staticmethod
def file_descriptor(request, response):
if not config.transcript['disabled']:
transcribe.transcribe(request)
return response
return transcribe_request(request, response, False)

@staticmethod
def _execute(function, return_function=None):
return_function = return_function or CTERAClient.fromxmlstr
try:
request, response = function()
return return_function(request, response)
except HTTPException as http_error:
client_error = CTERAClientException()
client_error.__dict__ = http_error.__dict__.copy()
raise client_error
return execute_request(function, return_function)


def execute_request(request_function, return_function):
"""
Execute an HTTP request
"""
try:
request, response = request_function()
return return_function(request, response)
except HTTPException as http_error:
client_error = CTERAClientException()
client_error.__dict__ = http_error.__dict__.copy()
raise client_error


def transcribe_request(request, response, transcribe_response=True):
"""
Transcribe the HTTP request
"""
if not config.transcript['disabled']:
if transcribe_response:
response = HTTPResponse(response)
transcribe.transcribe(request, response)
else:
transcribe.transcribe(request)
return response


class RESTClient:

def __init__(self, http_client=None, session_id_key=None):
self.http_client = http_client if http_client else HTTPClient(session_id_key)

def get(self, baseurl, path, params=None):
function = Command(HTTPClient.get, self.http_client, geturi(baseurl, path), params if params else {})
return self._execute(function)

def put(self, baseurl, path, data):
function = Command(HTTPClient.put, self.http_client, geturi(baseurl, path), ContentType.application_json, tojsonstr(data))
return self._execute(function)

def post(self, baseurl, path, data):
function = Command(HTTPClient.post, self.http_client, geturi(baseurl, path), ContentType.application_json, tojsonstr(data))
return self._execute(function)

def delete(self, baseurl, path):
function = Command(HTTPClient.delete, self.http_client, geturi(baseurl, path))
return self._execute(function)

@staticmethod
def fromjsonstr(request, response):
response = transcribe_request(request, response, True)
return fromjsonstr(response.text)

@staticmethod
def _execute(function):
return execute_request(function, RESTClient.fromjsonstr)


class MigrationClient(RESTClient):

XSRF_TOKEN_ID = 'x-mt-x'

def login(self, baseurl, path):
_, response = self.http_client.get(geturi(baseurl, path))
xsrf_token = response.headers.get(MigrationClient.XSRF_TOKEN_ID, None)
self.http_client.set_custom_headers({MigrationClient.XSRF_TOKEN_ID: xsrf_token})
40 changes: 39 additions & 1 deletion cterasdk/client/host.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
from ..common import Object
from ..convert import tojsonstr
from ..exception import HostUnreachable
from .cteraclient import CTERAClient
from .cteraclient import CTERAClient, MigrationClient, RESTClient
from ..exception import CTERAException


Expand Down Expand Up @@ -67,11 +67,49 @@ def __str__(self):
return tojsonstr(x)


class MigrationHost(NetworkHost):

def __init__(self, host, port, https, is_authenticated=None, http_client=None):
super().__init__(host, port, https)

def always_authenticated(self, function): # pylint: disable=unused-argument
return True
self._is_authenticated = is_authenticated if is_authenticated else always_authenticated
self._client = MigrationClient(http_client) if http_client else RESTClient()

@staticmethod
def from_ctera_host(ctera_host):
"""Create a RESTful host instance from an existing CTERA host instance"""
return MigrationHost(ctera_host.host(), ctera_host.port(), ctera_host.https(),
ctera_host._is_authenticated, ctera_host._ctera_client.http_client) # pylint: disable=protected-access

@authenticated
def login(self, path):
return self._client.login(self.baseurl(), path)

@authenticated
def get(self, path, params=None):
return self._client.get(self.baseurl(), path, params or {})

@authenticated
def put(self, path, value):
return self._client.put(self.baseurl(), path, value)

@authenticated
def post(self, path, value):
return self._client.post(self.baseurl(), path, value)

@authenticated
def delete(self, path):
return self._client.delete(self.baseurl(), path)


class CTERAHost(NetworkHost): # pylint: disable=too-many-public-methods

def __init__(self, host, port, https):
super().__init__(host, port, https)
self._ctera_client = CTERAClient(self._session_id_key)
self._ctera_migrate = MigrationHost.from_ctera_host(self)
self._session = None

@property
Expand Down
1 change: 1 addition & 0 deletions cterasdk/client/http.py
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ def geturi(baseurl, path):
class ContentType:
urlencoded = {'Content-Type': 'application/x-www-form-urlencoded'}
textplain = {'Content-Type': 'text/plain'}
application_json = {'Content-Type': 'application/json'}


class HttpClientBase():
Expand Down
3 changes: 1 addition & 2 deletions cterasdk/core/files/directory.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,8 +70,7 @@ def _process_response(response, path):
except ReservedName as error:
logging.getLogger().error('Reserved directory name. %s', {'name': path})
raise error
else:
logging.getLogger().info('Directory created. %s', {'path': path})
logging.getLogger().info('Directory created. %s', {'path': path})


def _process_error(response, path):
Expand Down
30 changes: 30 additions & 0 deletions cterasdk/edge/enum.py
Original file line number Diff line number Diff line change
Expand Up @@ -392,3 +392,33 @@ class SMBProtocol:
SMB3_11 = 'SMB3_11'
SMB2 = 'SMB2'
SMB3 = 'SMB3'


class SourceType:
"""
Source Host Type
:ivar str Windows: Windows Server
:ivar str ONTAP: NetApp ONTAP
:ivar str OneFS: Isilon OneFS
:ivar str Panzura: Panzura Freedom Filer
:ivar str SGRID9_SMB: NetApp StorageGRID 9
:ivar str SGRID11_SMB: NetApp StorageGRID 11
"""
Windows = 'windowsServer'
ONTAP = 'netapp'
OneFS = 'isilon'
Panzura = 'panzura'
SGRID9_SMB = 'storageGrid9'
SGRID11_SMB = 'storageGrid11'


class TaskType:
"""
Migration Tool Task Type
:ivar str Discovery: Discovery
:ivar str Migration: Migration
"""
Discovery = 0
Migration = 1
1 change: 1 addition & 0 deletions cterasdk/edge/login.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ def login(self, username, password):
try:
self._gateway.form_data('/login', {'username': username, 'password': password})
logging.getLogger().info("User logged in. %s", {'host': host, 'user': username})
self._gateway.mtool.login()
except CTERAException as error:
logging.getLogger().error("Login failed. %s", {'host': host, 'user': username})
raise error
Expand Down

0 comments on commit df1f05a

Please sign in to comment.