Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for StorageBoxes #38

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 1 commit
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
15 changes: 15 additions & 0 deletions hetzner/robot.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@

from hetzner import WebRobotError, RobotError
from hetzner.server import Server
from hetzner.storagebox import StorageBox
from hetzner.rdns import ReverseDNSManager
from hetzner.failover import FailoverManager
from hetzner.util.http import ValidatedHTTPSConnection
Expand Down Expand Up @@ -427,10 +428,24 @@ def get(self, ip):
def __iter__(self):
return iter([Server(self.conn, s) for s in self.conn.get('/server')])

class StorageBoxManager(object):
def __init__(self, conn):
self.conn = conn

def get(self, id_):
"""
Get storage boxes by providing its main id
"""
return StorageBox(self.conn, self.conn.get('/storagebox/{0}'.format(id_)))

def __iter__(self):
return iter([StorageBox(self.conn, s) for s in self.conn.get('/storagebox')])


class Robot(object):
def __init__(self, user, passwd):
self.conn = RobotConnection(user, passwd)
self.servers = ServerManager(self.conn)
self.storageboxes = StorageBoxManager(self.conn)
self.rdns = ReverseDNSManager(self.conn)
self.failover = FailoverManager(self.conn, self.servers)
118 changes: 118 additions & 0 deletions hetzner/storagebox.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
import logging

from datetime import datetime

__all__ = ['StorageBox', 'SubAccount', 'SubAccountManager']


class SubAccount(object):
def __init__(self, conn, box_id_, result):
self.conn = conn
self.box_id_ = box_id_
Copy link
Owner

Choose a reason for hiding this comment

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

Why did you call this box_id_ instead of just box_id?

Copy link
Author

Choose a reason for hiding this comment

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

I think it was to keep consistent with the StorageBox object, which uses self.id_. I could change id to box_id

self.update_info(result)

def update_info(self, result):
"""
Update the information of the subaccount.
"""
data = result['subaccount']

self.username = data['username']
self.accountid = data['accountid']
self.server = data['server']
self.homedirectory = data['homedirectory']
self.samba = data['samba']
self.ssh = data['ssh']
self.external_reachability = data['external_reachability']
self.webdav = data['webdav']
self.readonly = data['readonly']
self.createtime = datetime.strptime(data['createtime'], '%Y-%m-%d %H:%M:%S')
self.comment = data['comment']

def update(self, homedirectory, samba, ssh, external_reachability, webdav, readonly, comment):
result = self.conn.put('/storagebox/{0}/subaccount/{1}'.format(self.box_id_, self.username),
{'homedirectory': homedirectory,
'samba': samba,
'ssh': ssh,
'external_reachability': external_reachability,
'webdav': webdav,
'readonly': readonly,
'comment': comment})

return result
pajowu marked this conversation as resolved.
Show resolved Hide resolved

def reset_password(self):
result = self.conn.post('/storagebox/{0}/subaccount/{1}/password'.format(self.box_id_, self.username), None)
return result['password']

def delete(self):
self.conn.delete('/storagebox/{0}/subaccount/{1}'.format(self.box_id_, self.username))

def __repr__(self):
return "<SubAccount {0}>".format(self.username)


class SubAccountManager(object):
def __init__(self, conn, box_id_):
self.conn = conn
self.box_id_ = box_id_

def create(self, homedirectory, samba, ssh, external_reachability, webdav, readonly, comment):
result = self.conn.post('/storagebox/{0}/subaccount'.format(self.box_id_),
{'homedirectory': homedirectory,
'samba': samba,
'ssh': ssh,
'external_reachability': external_reachability,
'webdav': webdav,
'readonly': readonly,
'comment': comment})

return result

def delete(self, username):
self.conn.delete('/storagebox/{0}/subaccount/{1}'.format(self.box_id_, username))

def __iter__(self):
return iter([SubAccount(self.conn, self.box_id_, s) for s in self.conn.get('/storagebox/{0}/subaccount'.format(self.box_id_))])


class StorageBox(object):
def __init__(self, conn, result):
self.conn = conn
self.update_info(result)
self.subaccounts = SubAccountManager(self.conn, self.id_)
self.logger = logging.getLogger("StorageBox #{0}".format(self.id_))

def update_info(self, result=None):
"""
Updates the information of the current SubAccount instance either by
sending a new GET request or by parsing the response given by result.
"""
if result is None:
result = self.conn.get('/storagebox/{0}'.format(self.id_))
data = result['storagebox']

self.id_ = data['id']
self.login = data['login']
self.name = data['name']
self.product = data['product']
self.cancelled = data['cancelled']
self.locked = data['locked']
self.location = data['location']
self.linked_server = data['linked_server']
self.paid_until = datetime.strptime(data['paid_until'], '%Y-%m-%d')
if 'disk_quota' in data:
self.disk_quota = data['disk_quota']
self.disk_usage = data['disk_usage']
self.disk_usage_data = data['disk_usage_data']
self.disk_usage_snapshots = data['disk_usage_snapshots']
self.webdav = data['webdav']
self.samba = data['samba']
self.ssh = data['ssh']
self.external_reachability = data['external_reachability']
self.zfs = data['zfs']
self.server = data['server']
self.host_system = data['host_system']

def __repr__(self):
return "<{0} (#{1} {2})>".format(self.login, self.id_, self.product)
22 changes: 22 additions & 0 deletions hetznerctl
Original file line number Diff line number Diff line change
Expand Up @@ -339,6 +339,27 @@ class Config(SubCommand):
self.config.write(fp)


class ListStorageboxes(SubCommand):
command = 'list-storageboxes'
description = "List all storageboxes"

def execute(self, robot, parser, args):
for storagebox in robot.storageboxes:
info = {
'login': storagebox.login,
'product': storagebox.product
}

if storagebox.name != "":
info['name'] = storagebox.name

infolist = [u"{0}: {1}".format(key, val)
for key, val in info.items()]

self.putline(u"{0} ({1})".format(storagebox.login, u", ".join(infolist)))
Copy link
Owner

Choose a reason for hiding this comment

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

Just was about to merge this pull request and did a small final review and now wondering why you use Unicode literals here: Do you still use Python 2.x and if yes, what's the reason? I'm asking because I wanted to get rid of Python 2 support, but I might re-evaluate if there are still Python 2 users out there.

Copy link
Author

Choose a reason for hiding this comment

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

No, I just use python3. Not sure why I used them, maybe because they were used somewhere else?




pajowu marked this conversation as resolved.
Show resolved Hide resolved
def main():
subcommands = [
Config,
Expand All @@ -350,6 +371,7 @@ def main():
ReverseDNS,
Admin,
Failover,
ListStorageboxes
pajowu marked this conversation as resolved.
Show resolved Hide resolved
]

common_parser = argparse.ArgumentParser(
Expand Down