import os
import logging
from lib.gen_py.cloudfs.ttypes import *
class OxygenPathObject:
"""
Allows storing extra metadata of Oxygen API Objects (like path or modified timestamp)
and provides many helper methods for converting between Oxygen API Object types and
especially useful methods regarding Oxygen top level containers (i.e. Spaces, Volumes)
"""
oid = None # universal unique identifier because of o2object.id and o2object.oid
o2object = None
path = '' # path empty = root level
modified_timestamp = 0
def __init__( self , oid , o2object , path , modified_timestamp ):
self.oid = oid # more control over how objects can be compared (i.e. o2objects are sometimes id and sometimes oid)
self.o2object = o2object
self.path = path
self.modified_timestamp = modified_timestamp
def __eq__( self , other ): # TODO: not used and so may be unnecessary work
if not other:
return False
else:
return self.oid == other.oid
def __hash__(self): # TODO: not used and so may be unnecessary work
return hash( self.oid )
@staticmethod
def get_unicode_string( s ):
if s:
return s if type(s) is unicode else (s.decode('utf-8') if type(s) is str else unicode(s))
else:
return s
@staticmethod
def convert_to_local_file_path( local_destination_path , cloud_folder_path , object_name ):
local_destination_path = os.path.normpath( local_destination_path )
if cloud_folder_path == '':
converted_path = '' # do not convert blank into '.'
elif cloud_folder_path[0] == '/':
stripped_path = cloud_folder_path[1:] # strip any leading slashes
converted_path = os.path.normpath( stripped_path ) # i.e. for downloading from the cloud to Windows
else:
converted_path = os.path.normpath( cloud_folder_path )
new_path = os.path.join( converted_path , object_name ) # name is a single string so does not need conversion
new_absolute_full_local_path = os.path.join( local_destination_path , new_path )
return new_absolute_full_local_path
@staticmethod
def convert_space_info_to_api_object( api_space_info_object ):
"""
ApiSpaceInfo( capacity=0, name='space-name', writableDefault=False, oid='ff8080813cbbeafe013cf496a90e3392',
ownerOid='ff80808134f2fda80134f75ab6de0108', createdTimestamp=1361313179000L, utilized=0.00048828125,
ownerOxygenId='John-Enterprise-QA', storageName='Default John-Enterprise-QA Cloud', spaceDescription=None,
repositoryNodeId='repositorynode3', listed=False )
ApiObject( canManage=True, sizeInByte=0, name='many-folder-space', modifiedByUserName=None, canWrite=True,
repositoryNodeServiceId='repositorynode3', modifiedTimestamp=0, createdTimestamp=0, deleted=False, versionId=0,
ownerOxygenId='John-Enterprise-QA', spaceId='ff8080813cbbeafe013cf496a90e3392', parentId='', createdByUserName=None,
type=2, id='ff8080813cbbeafe013cf496a90e3392' )
"""
if not api_space_info_object:
raise ValueError( "api_space_info_object must exist" )
api_object = ApiObject( canManage = True , sizeInByte = 0 , name = api_space_info_object.name , canWrite = True ,
repositoryNodeServiceId = api_space_info_object.repositoryNodeId , modifiedTimestamp = 0 ,
createdTimestamp = 0, deleted = False , versionId = 0 , ownerOxygenId = api_space_info_object.ownerOxygenId ,
spaceId = api_space_info_object.oid , parentId = '' , type = ApiObjectType.SPACE , id = api_space_info_object.oid )
return api_object
@staticmethod
def convert_space_object_to_api_object( api_space_object ):
"""
ApiSpace(ownerUserOid='ff80808134f2fda80134f75ab6de0108', repoNodeServiceId='repositorynode3', managePermitted=True,
name='many-folder-space', writePermitted=True, ownerLoginId='John-Enterprise-QA', id='ff8080813cbbeafe013cf496a90e3392')
ApiObject( canManage=True, sizeInByte=0, name='many-folder-space', modifiedByUserName=None, canWrite=True,
repositoryNodeServiceId='repositorynode3', modifiedTimestamp=0, createdTimestamp=0, deleted=False, versionId=0,
ownerOxygenId='John-Enterprise-QA', spaceId='ff8080813cbbeafe013cf496a90e3392', parentId='', createdByUserName=None,
type=2, id='ff8080813cbbeafe013cf496a90e3392' )
"""
if not api_space_object:
raise ValueError( "api_space_object must exist" )
api_object = ApiObject( canManage = api_space_object.managePermitted , sizeInByte = 0 , name = api_space_object.name ,
canWrite = api_space_object.writePermitted , repositoryNodeServiceId = api_space_object.repoNodeServiceId ,
modifiedTimestamp = 0 , createdTimestamp = 0, deleted = False , versionId = 0 , ownerOxygenId = api_space_object.ownerUserOid ,
spaceId = api_space_object.id , parentId = '' , type = ApiObjectType.SPACE , id = api_space_object.id )
return api_object
@staticmethod
def get_account_space_info_listing( agent ):
if not agent:
raise ValueError( u"ERROR: agent cannot be null in {}".format( OxygenPathObject.get_account_volume_info_listing.__name__ ) )
return OxygenPathObject.get_top_level_container_info_listing( agent , 'findAllSpacesInAccount' )
@staticmethod
def get_account_volume_info_listing( agent ):
if not agent:
raise ValueError( u"ERROR: agent cannot be null in {}".format( OxygenPathObject.get_account_volume_info_listing.__name__ ) )
return OxygenPathObject.get_top_level_container_info_listing( agent , 'findAllUserVolumesInAccount' )
@staticmethod
def get_top_level_container_info_listing( agent , container_type ):
if not agent:
raise ValueError( u"ERROR: agent parameter cannot be null in {}()".format( OxygenPathObject.get_top_level_container_info_listing.__name__ ) )
if not container_type:
raise ValueError( u"ERROR: container_type parameter is not defined in {}()".format( OxygenPathObject.get_top_level_container_info_listing.__name__ ) )
full_listing = list()
index_timestamp = 0
previous_batch_last_space_id = '' # to verify when all items have been retrieved
current_batch = None
while True:
if container_type == 'findAllSpacesInAccount':
try:
current_batch = agent.findAllSpacesInAccount( index_timestamp ) # retrieves a batch of ApiSpaceInfo starting from the given createdTimestamp
except EOFError as error:
logging.warn( 'findAllSpacesInAccount() received an EOF from the cloud, retrying' )
current_batch = agent.findAllSpacesInAccount( index_timestamp ) # retrieves a batch of ApiSpaceInfo starting from the given createdTimestamp
elif container_type == 'findAllUserVolumesInAccount':
try:
current_batch = agent.findAllUserVolumesInAccount( index_timestamp ) # retrieves a batch of ApiSpaceInfo starting from the given createdTimestamp
except EOFError as error:
logging.warn( 'findAllSpacesInAccount() received an EOF from the cloud, retrying' )
current_batch = agent.findAllUserVolumesInAccount( index_timestamp ) # retrieves a batch of ApiSpaceInfo starting from the given createdTimestamp
else:
logging.error( u'list method for container_type is not recognized: {}'.format( container_type ) )
if not current_batch:
logging.warn( "None or 0 results returned from the cloud" )
break # not sure when nothing is returned but certainly a good reason to exit the loop
if previous_batch_last_space_id == current_batch[ -1 ].oid:
logging.info( 'search complete')
break
full_listing.extend( current_batch )
last_space_in_current_batch = current_batch[ -1 ]
index_timestamp = last_space_in_current_batch.createdTimestamp # used as a proxy for an iterator to get the next batch
previous_batch_last_space_id = last_space_in_current_batch.oid # update the end of loop condition
logging.debug( u'batch of results synchronized by: {} {}'.format( index_timestamp , previous_batch_last_space_id ) )
deduplicated_listing = OxygenPathObject.remove_duplicates_from_api_space_info_list( full_listing )
return deduplicated_listing
@staticmethod
def remove_duplicates_from_api_space_info_list( api_space_info_listing ):
"""
ApiSpaceInfo( capacity=0, name='space-name', writableDefault=False, oid='ff8080813cbbeafe013cf496a90e3392',
ownerOid='ff80808134f2fda80134f75ab6de0108', createdTimestamp=1361313179000L, utilized=0.00048828125,
ownerOxygenId='John-Enterprise-QA', storageName='Default John-Enterprise-QA Cloud', spaceDescription='description here',
repositoryNodeId='repositorynode3', listed=False )
"""
seen_oid_set = set()
deduplicated_listing = list()
for item in api_space_info_listing:
if item.oid not in seen_oid_set:
deduplicated_listing.append( item )
seen_oid_set.add( item.oid )
return deduplicated_listing
@staticmethod
def get_top_level_container_by_name( agent , target_name , container_type = '' ):
""" matching by name returns the last result from a complete list of all Spaces and Volumes in an Account
if no type is specified and a Space and Volume have the same name the Space will be returned
"""
result = None
if container_type == 'Space':
space_listing = OxygenPathObject.get_account_volume_info_listing( agent )
for space in space_listing:
if space.name == target_name:
result = space
elif container_type == 'Volume':
volume_listing = OxygenPathObject.get_account_volume_info_listing( agent )
for volume in volume_listing:
if volume.name == target_name:
result = volume
else:
volume_listing = OxygenPathObject.get_account_volume_info_listing( agent )
for volume in volume_listing:
if volume.name == target_name:
result = volume
space_listing = OxygenPathObject.get_account_volume_info_listing( agent )
for space in space_listing:
if space.name == target_name:
result = space
return result