Skip to content
Open
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
2 changes: 1 addition & 1 deletion SoftLayer/CLI/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@

from SoftLayer import Client, TimedClient, SoftLayerError, SoftLayerAPIError
from SoftLayer.consts import VERSION
from .helpers import CLIAbort, ArgumentError, format_output, KeyValueTable
from .helpers import CLIAbort, ArgumentError, InvalidInput, format_output, KeyValueTable
from .environment import Environment, InvalidCommand, InvalidModule


Expand Down
5 changes: 5 additions & 0 deletions SoftLayer/CLI/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,3 +23,8 @@ class ArgumentError(CLIAbort):
def __init__(self, msg, *args):
super(ArgumentError, self).__init__(msg, *args)
self.message = "Argument Error: %s" % msg

class InvalidInput(CLIAbort):
def _init_(self, msg, *args):
super(InvalidInput, self).__init__(msg, *args)
self.message = "InvalidInput: %s" % msg
4 changes: 2 additions & 2 deletions SoftLayer/CLI/helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

from SoftLayer.utils import NestedDict
from SoftLayer.CLI.environment import CLIRunnable
from .exceptions import CLIHalt, CLIAbort, ArgumentError
from .exceptions import CLIHalt, CLIAbort, ArgumentError, InvalidInput
from .formatting import (
Table, KeyValueTable, FormattedItem, SequentialOutput, confirm,
no_going_back, mb_to_gb, gb, listing, blank, format_output,
Expand All @@ -19,7 +19,7 @@
# Core/Misc
'CLIRunnable', 'NestedDict', 'FALSE_VALUES', 'resolve_id',
# Exceptions
'CLIAbort', 'CLIHalt', 'ArgumentError',
'CLIAbort', 'CLIHalt', 'ArgumentError', 'InvalidInput',
# Formatting
'Table', 'KeyValueTable', 'FormattedItem', 'SequentialOutput',
'valid_response', 'confirm', 'no_going_back', 'mb_to_gb', 'gb',
Expand Down
289 changes: 283 additions & 6 deletions SoftLayer/CLI/modules/iscsi.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,32 @@
Manage iSCSI targets

The available commands are:
list List iSCSI targets
"""
# :license: MIT, see LICENSE for more details.
list List iSCSI targets
create Create iSCSI target
detail Output details about iSCSI
cancel cancel iSCSI target
order_snapshot_space Orders space for snapshots
create_snapshot Create snapshot of iSCSI
delete_snapshot Delete iSCSI snapshot
restore_volume Restores volume from existing snapshot
list_snapshots List Snapshots of given iscsi

from SoftLayer.CLI import CLIRunnable, Table, FormattedItem
from SoftLayer.CLI.helpers import NestedDict, blank
"""
from SoftLayer.utils import lookup
from SoftLayer.CLI import (
CLIRunnable, Table, no_going_back, confirm, mb_to_gb, listing,
FormattedItem)
from SoftLayer.CLI.helpers import (
CLIAbort, ArgumentError, InvalidInput, NestedDict, blank, resolve_id, KeyValueTable,
update_with_template_args, FALSE_VALUES, export_to_template,
active_txn, transaction_status)
from SoftLayer import iSCSIManager


class ListISCSI(CLIRunnable):

"""
usage: sl iscsi list [options]
usage: sl iscsi list [options]

List iSCSI accounts
"""
Expand Down Expand Up @@ -48,3 +63,265 @@ def execute(self, args):
n.get('serviceResourceBackendIpAddress', blank())])

return t


class CreateiSCSI(CLIRunnable):

"""
usage: sl iscsi create --size=SIZE --dc=DC [options]

Order/create an iSCSI storage.

Required:
--size=SIZE Size
--dc=DC Datacenter
"""
action = 'create'
options = ['confirm']
required_params = ['--size', '--dc']

def execute(self, args):
iscsi = iSCSIManager(self.client)

self._validate_create_args(args)

size, location = self._parse_create_args(args)
location = self._get_location_id(location)
items = iscsi.find_items(int(size))
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Indentation error here. Avoid those with :set autoindent in vim.


iscsi.order_iscsi(items, size, location)

def _validate_create_args(self, args):
invalid_args = [k for k in self.required_params if args.get(k) is None]
if invalid_args:
raise ArgumentError('Missing required options: %s'
% ','.join(invalid_args))

def _parse_create_args(self, args):

size = int(args['--size'])
location = str(args['--dc'])
return size, location

def _get_location_id(self, location):
datacenters = self.client['Location_Datacenter'].getDatacenters(
mask='mask[longName,id,name]')
for dc in datacenters:
if dc['name'] == location:
self.location = dc['id']
return self.location
raise InvalidInput('Inavlid datacenter name: %s'
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we raise ArgumentError here? Instead of inventing new object for same purpose, can we reuse existing one with appropriate message?

% location)


class CanceliSCSI(CLIRunnable):

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

autopep8 adds space after a class which is not accepted by SL team, please remove those manually.

"""
usage: sl iscsi [--immediate] [--reason] cancel <identifier> [options]

Cancel iSCSI Storage

options :
--immediate Cancels the iSCSI immediately (instead of on the billing
anniversary)
--reason Reason for cancellation

Prompt Options:
-y, --really Confirm all prompt actions
"""
action = 'cancel'
options = ['confirm']

def execute(self, args):
iscsi = iSCSIManager(self.client)
iscsi_id = resolve_id(
iscsi.resolve_ids,
args.get('<identifier>'),
'iSCSI')
immediate = args.get('--immediate', False)
reason = args.get('--reason', str('No longer needed'))
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No need to cast string to string. remove str() for a 'string'


if args['--really'] or no_going_back(iscsi_id):
iscsi.cancel_iscsi(iscsi_id, reason, immediate)
else:
CLIAbort('Aborted')


class IscsiDetails(CLIRunnable):

"""
usage: sl iscsi detail [--passwords] <identifier> [options]

Get details for a iSCSI

Options:
--passwords Show passwords


"""
action = 'detail'

def execute(self, args):
iscsi = iSCSIManager(self.client)
t = KeyValueTable(['Name', 'Value'])
t.align['Name'] = 'r'
t.align['Value'] = 'l'

iscsi_id = resolve_id(
iscsi.resolve_ids,
args.get('<identifier>'),
'iSCSI')
result = iscsi.get_iscsi(iscsi_id)
result = NestedDict(result)

t.add_row(['id', result['id']])
t.add_row(['serviceResourceName', result['serviceResourceName']])
t.add_row(['createDate', result['createDate']])
t.add_row(['nasType', result['nasType']])
t.add_row(['capacityGb', result['capacityGb']])
if result['snapshotCapacityGb']:
t.add_row(['snapshotCapacityGb', result['snapshotCapacityGb']])
t.add_row(['mountableFlag', result['mountableFlag']])
t.add_row(['serviceResourceBackendIpAddress', result['serviceResourceBackendIpAddress']])
t.add_row(['price', result['billingItem']['recurringFee']])
t.add_row(['BillingItemId', result['billingItem']['id']])
if result.get('notes'):
t.add_row(['notes', result['notes']])

if args.get('--passwords'):
pass_table = Table(['username', 'password'])
pass_table.add_row([result['username'], result['password']])
t.add_row(['users', pass_table])

return t


class IscsiCreateSnapshot(CLIRunnable):

"""
usage: sl iscsi create_snapshot <identifier> [options]

create an iSCSI snapshot.

"""
action = 'create_snapshot'

def execute(self, args):
iscsi = iSCSIManager(self.client)
iscsi_id = resolve_id(iscsi.resolve_ids,
args.get('<identifier>'),
'iSCSI')
iscsi.create_snapshot(iscsi_id)


class OrderIscsiSpace(CLIRunnable):

"""
usage: sl iscsi order_snapshot_space [--capacity=Capacity...] [options]
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

have you missed <identifier> here?


Order iSCSI snapshot space.

Required :
--capacity = Snapshot Capacity
"""

action = 'order_snapshot_space'
required_params = ['--capacity']

def execute(self, args):
iscsi = iSCSIManager(self.client)
invalid_args = [k for k in self.required_params if args.get(k) is None]
if invalid_args:
raise ArgumentError('Missing required options: %s'
% ','.join(invalid_args))
iscsi_id = resolve_id(
iscsi.resolve_ids,
args.get('<identifier>'),
'iSCSI')
item_price = iscsi.find_space(int(args['--capacity'][0]))
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

find_item_price seems to be more appropriate than find_space.

result = iscsi.get_iscsi(
iscsi_id,
mask='mask[id,capacityGb,serviceResource[datacenter]]')
snapshotSpaceOrder = {
'complexType':
'SoftLayer_Container_Product_Order_Network_Storage_Iscsi_SnapshotSpace',
'location': result['serviceResource']['datacenter']['id'],
'packageId': 0,
'prices': [{'id': item_price}],
'quantity': 1,
'volumeId': iscsi_id}
iscsi.Order_snapshot_space(**snapshotSpaceOrder)


class IscsiDeleteSnapshot(CLIRunnable):

"""
usage: sl iscsi delete_snapshot <identifier> [options]

Delete iSCSI snapshot.

"""
action = 'delete_snapshot'

def execute(self, args):
iscsi = iSCSIManager(self.client)
snapshot_id = resolve_id(
iscsi.resolve_ids,
args.get('<identifier>'),
'Snapshot')
iscsi.delete_snapshot(snapshot_id)


class RestoreVolumefromSnapshot(CLIRunnable):

"""
usage: sl iscsi restore_volume <identifier>
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here we will need 2 arguments both <snapshot_id> and <volume_id>, as there can be more than one snapshot for a given volume.


restores volume from existing snapshot.

"""
action = 'restore_volume'

def execute(self, args):
iscsi = iSCSIManager(self.client)
snapshot_id = resolve_id(
iscsi.resolve_ids,
args.get('<identifier>'),
'Snapshot')
result = iscsi.get_iscsi(snapshot_id,mask='mask[parentPartnerships.volumeId]')
volume_id = result['parentPartnerships'][0]['volumeId']
iscsi.restore_from_snapshot(volume_id, snapshot_id)

class ListISCSISnapshots(CLIRunnable):

"""
usage: sl iscsi list_snapshots <identifier>

List iSCSI Snapshots
"""
action = 'list_snapshots'

def execute(self, args):
mgr = iSCSIManager(self.client)
iscsi_id = resolve_id(mgr.resolve_ids,args.get('<identifier>'),'iSCSI')
iscsi = self.client['Network_Storage_Iscsi']
snapshots = iscsi.getPartnerships(mask='volumeId,partnerVolumeId,createDate,type', id = iscsi_id)
print snapshots
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just before commit, always make sure you do not have print statements.

snapshots = [NestedDict(n) for n in snapshots]

t = Table([
'id',
'createDate',
'name',
'description',
])

for n in snapshots:
t.add_row([
n['partnerVolumeId'],
n['createDate'],
n['type']['name'],
n['type']['description'],
])
return t

4 changes: 2 additions & 2 deletions SoftLayer/managers/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
from SoftLayer.managers.sshkey import SshKeyManager
from SoftLayer.managers.ssl import SSLManager
from SoftLayer.managers.ticket import TicketManager

from SoftLayer.managers.iscsi import iSCSIManager
__all__ = ['CCIManager', 'DNSManager', 'FirewallManager', 'HardwareManager',
'ImageManager', 'MessagingManager', 'MetadataManager',
'NetworkManager', 'SshKeyManager', 'SSLManager', 'TicketManager']
'NetworkManager', 'SshKeyManager', 'SSLManager', 'TicketManager', 'iSCSIManager']
Loading