Browse Source

1.2.0: Ran black

v2
Teran McKinney 1 year ago
parent
commit
4166b2b675
  1. 53
      setup.py
  2. 2
      sporestackv2/__init__.py
  3. 338
      sporestackv2/api_client.py
  4. 20
      sporestackv2/api_client_test.py
  5. 652
      sporestackv2/client.py
  6. 6
      sporestackv2/client_test.py
  7. 30
      sporestackv2/utilities.py
  8. 24
      sporestackv2/utilities_test.py
  9. 93
      sporestackv2/validate.py
  10. 86
      sporestackv2/validate_test.py
  11. 2
      sporestackv2/version.py

53
setup.py

@ -5,42 +5,45 @@ from setuptools import setup
# Not sure how necessary this is. Would be nice to just
# import .sporestackv2.__version__
VERSION = None
with open('sporestackv2/version.py') as f:
with open("sporestackv2/version.py") as f:
for line in f:
if line.startswith('__version__'):
VERSION = line.replace("'", '').split('=')[1].strip()
if line.startswith("__version__"):
VERSION = line.replace("'", "").split("=")[1].strip()
break
if VERSION is None:
raise ValueError('__version__ not found in __init__.py')
raise ValueError("__version__ not found in __init__.py")
DOWNLOAD_URL = 'https://github.com/sporestack/sporestack-python/tarball/{}'
DOWNLOAD_URL = "https://github.com/sporestack/sporestack-python/tarball/{}"
DESCRIPTION = 'SporeStack.com library and client. Launch servers with Bitcoin.'
KEYWORDS = ['bitcoin', 'bitcoincash', 'bitcoinsv', 'servers', 'infrastructure',
'vps', 'virtual private server']
DESCRIPTION = "SporeStack.com library and client. Launch servers with Bitcoin."
KEYWORDS = [
"bitcoin",
"bitcoincash",
"bitcoinsv",
"servers",
"infrastructure",
"vps",
"virtual private server",
]
setup(
python_requires='>=3.3',
name='sporestack',
python_requires=">=3.3",
name="sporestack",
version=VERSION,
author='SporeStack',
author_email='admin@sporestack.com',
author="SporeStack",
author_email="admin@sporestack.com",
description=DESCRIPTION,
keywords=KEYWORDS,
license='Unlicense',
url='https://sporestack.com/',
license="Unlicense",
url="https://sporestack.com/",
download_url=DOWNLOAD_URL.format(VERSION),
packages=['sporestackv2'],
packages=["sporestackv2"],
install_requires=[
'pyqrcode',
'requests[socks]>=2.22.0',
'aaargh',
'walkingliberty',
'sshpubkeys'
"pyqrcode",
"requests[socks]>=2.22.0",
"aaargh",
"walkingliberty",
"sshpubkeys",
],
entry_points={
'console_scripts': [
'sporestackv2 = sporestackv2.client:main'
]
}
entry_points={"console_scripts": ["sporestackv2 = sporestackv2.client:main"]},
)

2
sporestackv2/__init__.py

@ -4,6 +4,6 @@ from . import utilities
from . import validate
from . import version
__all__ = ['api_client', 'client', 'utilities', 'validate']
__all__ = ["api_client", "client", "utilities", "validate"]
__version__ = version.__version__

338
sporestackv2/api_client.py

@ -13,17 +13,17 @@ LATEST_API_VERSION = 2
GET_TIMEOUT = 60
POST_TIMEOUT = 90
USE_TOR_PROXY = 'auto'
TOR_PROXY = 'socks5h://127.0.0.1:9050'
USE_TOR_PROXY = "auto"
TOR_PROXY = "socks5h://127.0.0.1:9050"
# For requests module
TOR_PROXY_REQUESTS = {'http': TOR_PROXY, 'https': TOR_PROXY}
TOR_PROXY_REQUESTS = {"http": TOR_PROXY, "https": TOR_PROXY}
def validate_use_tor_proxy(use_tor_proxy):
if isinstance(use_tor_proxy, bool):
return True
if isinstance(use_tor_proxy, str):
if use_tor_proxy == 'auto':
if use_tor_proxy == "auto":
return True
raise ValueError('use_tor_proxy must be True, False, or "auto"')
@ -40,54 +40,52 @@ def is_onion_url(url):
This can be optimized a lot.
"""
try:
url_parts = url.split('/')
url_parts = url.split("/")
domain = url_parts[2]
tld = domain.split('.')[-1]
if tld == 'onion':
tld = domain.split(".")[-1]
if tld == "onion":
return True
except Exception:
pass
return False
def api_request(url,
json_params=None,
get_params=None,
retry=False,
use_tor_proxy=USE_TOR_PROXY):
def api_request(
url, json_params=None, get_params=None, retry=False, use_tor_proxy=USE_TOR_PROXY
):
validate_use_tor_proxy(use_tor_proxy)
proxies = {}
if use_tor_proxy is True:
proxies = TOR_PROXY_REQUESTS
elif use_tor_proxy == 'auto':
elif use_tor_proxy == "auto":
if is_onion_url(url) is True:
msg = 'use_tor_proxy is "auto" and we have a .onion url, '
msg += 'using local Tor SOCKS proxy.'
msg += "using local Tor SOCKS proxy."
logging.debug(msg)
proxies = TOR_PROXY_REQUESTS
try:
if json_params is None:
request = requests.get(url,
params=get_params,
timeout=GET_TIMEOUT,
proxies=proxies)
request = requests.get(
url, params=get_params, timeout=GET_TIMEOUT, proxies=proxies
)
else:
request = requests.post(url,
json=json_params,
timeout=POST_TIMEOUT,
proxies=proxies)
request = requests.post(
url, json=json_params, timeout=POST_TIMEOUT, proxies=proxies
)
except Exception as e:
if retry is True:
logging.warning('Got an error, but retrying: {}'.format(e))
logging.warning("Got an error, but retrying: {}".format(e))
sleep(5)
# Try again.
return api_request(url,
json_params=json_params,
get_params=get_params,
retry=retry,
use_tor_proxy=use_tor_proxy)
return api_request(
url,
json_params=json_params,
get_params=get_params,
retry=retry,
use_tor_proxy=use_tor_proxy,
)
else:
raise
@ -95,46 +93,48 @@ def api_request(url,
if status_code_first_digit == 2:
try:
request_dict = request.json()
if 'latest_api_version' in request_dict:
if request_dict['latest_api_version'] > LATEST_API_VERSION:
logging.warning('New API version may be available.')
if "latest_api_version" in request_dict:
if request_dict["latest_api_version"] > LATEST_API_VERSION:
logging.warning("New API version may be available.")
return request_dict
except Exception:
return request.content
elif status_code_first_digit == 4:
if request.status_code == 415:
raise NotImplementedError(request.content.decode('utf-8'))
raise NotImplementedError(request.content.decode("utf-8"))
else:
logging.debug('Status code: {}'.format(request.status_code))
raise ValueError(request.content.decode('utf-8'))
logging.debug("Status code: {}".format(request.status_code))
raise ValueError(request.content.decode("utf-8"))
elif status_code_first_digit == 5:
if retry is True:
logging.warning(request.content.decode('utf-8'))
logging.warning('Got a 500, retrying in 5 seconds...')
logging.warning(request.content.decode("utf-8"))
logging.warning("Got a 500, retrying in 5 seconds...")
sleep(5)
# Try again if we get a 500
return api_request(url,
json_params=json_params,
get_params=get_params,
retry=retry,
use_tor_proxy=use_tor_proxy)
return api_request(
url,
json_params=json_params,
get_params=get_params,
retry=retry,
use_tor_proxy=use_tor_proxy,
)
else:
raise Exception(str(request.content))
else:
# Not sure why we'd get this.
request.raise_for_status()
raise Exception('Stuff broke strangely.')
raise Exception("Stuff broke strangely.")
def normalize_argument(argument):
"""
Helps normalize arguments from aaargh that may not be what we want.
"""
if argument == 'False':
if argument == "False":
return False
elif argument == 'True':
elif argument == "True":
return True
elif argument == 'None':
elif argument == "None":
return None
else:
return argument
@ -145,35 +145,37 @@ def get_url(api_endpoint, host, target):
Has nothing to do with GET requests.
"""
if api_endpoint is None:
api_endpoint = 'http://{}'.format(host)
return '{}/v{}/{}'.format(api_endpoint, LATEST_API_VERSION, target)
def launch(machine_id,
days,
disk,
memory,
ipv4,
ipv6,
bandwidth,
currency,
region=None,
ipxescript=None,
operating_system=None,
ssh_key=None,
organization=None,
cores=1,
managed=False,
override_code=None,
settlement_token=None,
qemuopts=None,
hostaccess=False,
api_endpoint=None,
host=None,
want_topup=False,
retry=False,
affiliate_amount=None,
affiliate_token=None):
api_endpoint = "http://{}".format(host)
return "{}/v{}/{}".format(api_endpoint, LATEST_API_VERSION, target)
def launch(
machine_id,
days,
disk,
memory,
ipv4,
ipv6,
bandwidth,
currency,
region=None,
ipxescript=None,
operating_system=None,
ssh_key=None,
organization=None,
cores=1,
managed=False,
override_code=None,
settlement_token=None,
qemuopts=None,
hostaccess=False,
api_endpoint=None,
host=None,
want_topup=False,
retry=False,
affiliate_amount=None,
affiliate_token=None,
):
"""
Only ipxescript or operating_system + ssh_key can be None.
"""
@ -195,57 +197,63 @@ def launch(machine_id,
validate.ssh_key(ssh_key)
validate.affiliate_amount(affiliate_amount)
json_params = {'machine_id': machine_id,
'days': days,
'disk': disk,
'memory': memory,
'cores': cores,
'managed': managed,
'currency': currency,
'region': region,
'organization': organization,
'bandwidth': bandwidth,
'ipv4': ipv4,
'ipv6': ipv6,
'override_code': override_code,
'settlement_token': settlement_token,
'qemuopts': qemuopts,
'hostaccess': hostaccess,
'ipxescript': ipxescript,
'operating_system': operating_system,
'ssh_key': ssh_key,
'want_topup': want_topup,
'host': host,
'affiliate_amount': affiliate_amount,
'affiliate_token': affiliate_token}
url = get_url(api_endpoint=api_endpoint, host=host, target='launch')
json_params = {
"machine_id": machine_id,
"days": days,
"disk": disk,
"memory": memory,
"cores": cores,
"managed": managed,
"currency": currency,
"region": region,
"organization": organization,
"bandwidth": bandwidth,
"ipv4": ipv4,
"ipv6": ipv6,
"override_code": override_code,
"settlement_token": settlement_token,
"qemuopts": qemuopts,
"hostaccess": hostaccess,
"ipxescript": ipxescript,
"operating_system": operating_system,
"ssh_key": ssh_key,
"want_topup": want_topup,
"host": host,
"affiliate_amount": affiliate_amount,
"affiliate_token": affiliate_token,
}
url = get_url(api_endpoint=api_endpoint, host=host, target="launch")
return api_request(url=url, json_params=json_params, retry=retry)
def topup(machine_id,
days,
currency,
settlement_token=None,
override_code=None,
api_endpoint=None,
host=None,
retry=False,
affiliate_amount=None,
affiliate_token=None):
def topup(
machine_id,
days,
currency,
settlement_token=None,
override_code=None,
api_endpoint=None,
host=None,
retry=False,
affiliate_amount=None,
affiliate_token=None,
):
validate.machine_id(machine_id)
validate.currency(currency)
validate.affiliate_amount(affiliate_amount)
json_params = {'machine_id': machine_id,
'days': days,
'settlement_token': settlement_token,
'currency': currency,
'host': host,
'override_code': override_code,
'affiliate_amount': affiliate_amount,
'affiliate_token': affiliate_token}
url = get_url(api_endpoint=api_endpoint, host=host, target='topup')
json_params = {
"machine_id": machine_id,
"days": days,
"settlement_token": settlement_token,
"currency": currency,
"host": host,
"override_code": override_code,
"affiliate_amount": affiliate_amount,
"affiliate_token": affiliate_token,
}
url = get_url(api_endpoint=api_endpoint, host=host, target="topup")
return api_request(url=url, json_params=json_params, retry=retry)
@ -255,8 +263,8 @@ def start(host, machine_id, api_endpoint=None):
"""
validate.machine_id(machine_id)
url = get_url(api_endpoint=api_endpoint, host=host, target='start')
json_params = {'machine_id': machine_id, 'host': host}
url = get_url(api_endpoint=api_endpoint, host=host, target="start")
json_params = {"machine_id": machine_id, "host": host}
api_request(url, json_params=json_params)
return True
@ -267,8 +275,8 @@ def stop(host, machine_id, api_endpoint=None):
"""
validate.machine_id(machine_id)
url = get_url(api_endpoint=api_endpoint, host=host, target='stop')
json_params = {'machine_id': machine_id, 'host': host}
url = get_url(api_endpoint=api_endpoint, host=host, target="stop")
json_params = {"machine_id": machine_id, "host": host}
api_request(url, json_params=json_params)
return True
@ -279,8 +287,8 @@ def delete(host, machine_id, api_endpoint=None):
"""
validate.machine_id(machine_id)
url = get_url(api_endpoint=api_endpoint, host=host, target='delete')
json_params = {'machine_id': machine_id, 'host': host}
url = get_url(api_endpoint=api_endpoint, host=host, target="delete")
json_params = {"machine_id": machine_id, "host": host}
api_request(url, json_params=json_params)
return True
@ -292,8 +300,8 @@ def sshhostname(host, machine_id, api_endpoint=None):
"""
validate.machine_id(machine_id)
url = get_url(api_endpoint=api_endpoint, host=host, target='sshhostname')
get_params = {'machine_id': machine_id, 'host': host}
url = get_url(api_endpoint=api_endpoint, host=host, target="sshhostname")
get_params = {"machine_id": machine_id, "host": host}
return api_request(url, get_params=get_params)
@ -303,8 +311,8 @@ def info(host, machine_id, api_endpoint=None):
"""
validate.machine_id(machine_id)
url = get_url(api_endpoint=api_endpoint, host=host, target='info')
get_params = {'machine_id': machine_id, 'host': host}
url = get_url(api_endpoint=api_endpoint, host=host, target="info")
get_params = {"machine_id": machine_id, "host": host}
return api_request(url, get_params=get_params)
@ -316,15 +324,13 @@ def ipxescript(host, machine_id, ipxescript=None, api_endpoint=None):
validate.machine_id(machine_id)
if ipxescript is None:
if __name__ == '__main__':
if __name__ == "__main__":
ipxescript = sys.stdin.read()
else:
raise ValueError('ipxescript must be set.')
raise ValueError("ipxescript must be set.")
url = get_url(api_endpoint=api_endpoint, host=host, target='ipxescript')
json_params = {'machine_id': machine_id,
'host': host,
'ipxescript': ipxescript}
url = get_url(api_endpoint=api_endpoint, host=host, target="ipxescript")
json_params = {"machine_id": machine_id, "host": host, "ipxescript": ipxescript}
return api_request(url, json_params=json_params)
@ -335,10 +341,8 @@ def bootorder(host, machine_id, bootorder, api_endpoint=None):
validate.machine_id(machine_id)
validate.bootorder(bootorder)
url = get_url(api_endpoint=api_endpoint, host=host, target='ipxescript')
json_params = {'machine_id': machine_id,
'host': host,
'bootorder': bootorder}
url = get_url(api_endpoint=api_endpoint, host=host, target="ipxescript")
json_params = {"machine_id": machine_id, "host": host, "bootorder": bootorder}
return api_request(url, json_params=json_params)
@ -346,7 +350,7 @@ def host_info(host, api_endpoint=None):
"""
Returns info about the host.
"""
url = get_url(api_endpoint=api_endpoint, host=host, target='host_info')
url = get_url(api_endpoint=api_endpoint, host=host, target="host_info")
return api_request(url)
@ -356,55 +360,53 @@ def serialconsole(host, machine_id):
"""
validate.machine_id(machine_id)
command = '/usr/bin/ssh'
command = "/usr/bin/ssh"
arguments = []
arguments.append(command)
arguments.append('-t')
arguments.append('vmmanagement@{}'.format(host))
arguments.append('-p')
arguments.append('1060')
arguments.append('serialconsole {}'.format(machine_id))
arguments.append("-t")
arguments.append("vmmanagement@{}".format(host))
arguments.append("-p")
arguments.append("1060")
arguments.append("serialconsole {}".format(machine_id))
logging.info(command, arguments)
os.execv(command, arguments)
def settlement_token_enable(settlement_token,
cents,
currency,
api_endpoint=None,
retry=False):
def settlement_token_enable(
settlement_token, cents, currency, api_endpoint=None, retry=False
):
validate.settlement_token(settlement_token)
validate.cents(cents)
validate.currency(currency)
json_params = {'settlement_token': settlement_token,
'cents': cents,
'currency': currency}
url = api_endpoint + '/settlement/enable'
json_params = {
"settlement_token": settlement_token,
"cents": cents,
"currency": currency,
}
url = api_endpoint + "/settlement/enable"
return api_request(url=url, json_params=json_params, retry=retry)
def settlement_token_add(settlement_token,
cents,
currency,
api_endpoint=None,
retry=False):
def settlement_token_add(
settlement_token, cents, currency, api_endpoint=None, retry=False
):
validate.settlement_token(settlement_token)
validate.cents(cents)
validate.currency(currency)
json_params = {'settlement_token': settlement_token,
'cents': cents,
'currency': currency}
url = api_endpoint + '/settlement/add'
json_params = {
"settlement_token": settlement_token,
"cents": cents,
"currency": currency,
}
url = api_endpoint + "/settlement/add"
return api_request(url=url, json_params=json_params, retry=retry)
def settlement_token_balance(settlement_token,
api_endpoint=None,
retry=False):
def settlement_token_balance(settlement_token, api_endpoint=None, retry=False):
validate.settlement_token(settlement_token)
get_params = {'settlement_token': settlement_token}
url = api_endpoint + '/settlement/balance'
get_params = {"settlement_token": settlement_token}
url = api_endpoint + "/settlement/balance"
return api_request(url=url, get_params=get_params, retry=retry)

20
sporestackv2/api_client_test.py

@ -6,9 +6,9 @@ import pytest
def test_validate_validate_use_tor_proxy():
assert api_client.validate_use_tor_proxy(True) is True
assert api_client.validate_use_tor_proxy(False) is True
assert api_client.validate_use_tor_proxy('auto') is True
assert api_client.validate_use_tor_proxy("auto") is True
with pytest.raises(ValueError):
api_client.validate_use_tor_proxy('some string')
api_client.validate_use_tor_proxy("some string")
with pytest.raises(ValueError):
api_client.validate_use_tor_proxy(1)
with pytest.raises(ValueError):
@ -16,14 +16,14 @@ def test_validate_validate_use_tor_proxy():
def test_is_onion_url():
onion_url = 'http://spore64i5sofqlfz5gq2ju4msgzojjwifls7'
onion_url += 'rok2cti624zyq3fcelad.onion/v2/'
onion_url = "http://spore64i5sofqlfz5gq2ju4msgzojjwifls7"
onion_url += "rok2cti624zyq3fcelad.onion/v2/"
assert api_client.is_onion_url(onion_url) is True
# This is a good, unusual test.
onion_url = 'https://www.facebookcorewwwi.onion/'
onion_url = "https://www.facebookcorewwwi.onion/"
assert api_client.is_onion_url(onion_url) is True
assert api_client.is_onion_url('http://domain.com') is False
assert api_client.is_onion_url('domain.com') is False
assert api_client.is_onion_url('http://onion.domain.com/.onion/') is False
assert api_client.is_onion_url('http://me.me/file.onion/') is False
assert api_client.is_onion_url('http://me.me/file.onion') is False
assert api_client.is_onion_url("http://domain.com") is False
assert api_client.is_onion_url("domain.com") is False
assert api_client.is_onion_url("http://onion.domain.com/.onion/") is False
assert api_client.is_onion_url("http://me.me/file.onion/") is False
assert api_client.is_onion_url("http://me.me/file.onion") is False

652
sporestackv2/client.py

@ -6,6 +6,7 @@ Cleaner interface into api_client, for the most part.
import json
import sys
# FUTURE PYTHON 3.6: import secrets
import os
import logging
@ -24,13 +25,14 @@ cli = aaargh.App()
logging.basicConfig(level=logging.INFO)
CLEARNET_ENDPOINT = 'https://api.sporestack.com'
TOR_ENDPOINT = 'http://spore64'\
'i5sofqlfz5gq2ju4msgzojjwifls7rok2cti624zyq3fcelad.onion'
CLEARNET_ENDPOINT = "https://api.sporestack.com"
TOR_ENDPOINT = (
"http://spore64" "i5sofqlfz5gq2ju4msgzojjwifls7rok2cti624zyq3fcelad.onion"
)
API_ENDPOINT = CLEARNET_ENDPOINT
WAITING_PAYMENT_TO_PROCESS = 'Waiting for payment to process...'
WAITING_PAYMENT_TO_PROCESS = "Waiting for payment to process..."
def i_am_root():
@ -54,92 +56,88 @@ def random_machine_id():
return machine_id
def make_payment(currency,
uri,
usd=None,
walkingliberty_wallet=None):
def make_payment(currency, uri, usd=None, walkingliberty_wallet=None):
if walkingliberty_wallet is not None:
walkingliberty = WalkingLiberty(currency)
txid = walkingliberty.pay(private_key=walkingliberty_wallet,
uri=uri)
logging.debug('WalkingLiberty txid: {}'.format(txid))
txid = walkingliberty.pay(private_key=walkingliberty_wallet, uri=uri)
logging.debug("WalkingLiberty txid: {}".format(txid))
else:
premessage = '''Payment URI: {}
premessage = """Payment URI: {}
Pay *exactly* the specified amount. No more, no less. Pay within
one hour at the very most.
Resize your terminal and try again if QR code above is not readable.
Press ctrl+c to abort.'''
Press ctrl+c to abort."""
message = premessage.format(uri)
qr = pyqrcode.create(uri)
print(qr.terminal(module_color='black',
background='white',
quiet_zone=1))
print(qr.terminal(module_color="black", background="white", quiet_zone=1))
print(message)
if usd is not None:
print('Approximate price in USD: {}'.format(usd))
input('[Press enter once you have made payment.]')
print("Approximate price in USD: {}".format(usd))
input("[Press enter once you have made payment.]")
# FIXME: ordering...
@cli.cmd
@cli.cmd_arg('vm_hostname')
@cli.cmd_arg('--host', type=str, default=None)
@cli.cmd_arg('--hostaccess', type=bool, default=False)
@cli.cmd_arg('--save', type=bool, default=True)
@cli.cmd_arg('--override_code', type=str, default=None)
@cli.cmd_arg('--region', type=str, default=None)
@cli.cmd_arg('--managed', type=bool, default=False)
@cli.cmd_arg('--currency', type=str, default=None)
@cli.cmd_arg('--settlement_token', type=str, default=None)
@cli.cmd_arg('--cores', type=int, default=1)
@cli.cmd_arg('--memory', type=int, default=1)
@cli.cmd_arg('--bandwidth', type=int, default=1)
@cli.cmd_arg('--ipv4', default='/32')
@cli.cmd_arg('--ipv6', default='/128')
@cli.cmd_arg('--disk', type=int, default=5)
@cli.cmd_arg('--days', type=int, required=True)
@cli.cmd_arg('--qemuopts', type=str, default=None)
@cli.cmd_arg('--walkingliberty_wallet', type=str, default=None)
@cli.cmd_arg('--api_endpoint', type=str, default=API_ENDPOINT)
@cli.cmd_arg('--want_topup', type=bool, default=False)
@cli.cmd_arg('--organization', type=str, default=None)
@cli.cmd_arg('--ipxescript', type=str, default=None)
@cli.cmd_arg('--ipxescript_stdin', type=bool, default=False)
@cli.cmd_arg('--ipxescript_file', type=str, default=None)
@cli.cmd_arg('--operating_system', type=str, default=None)
@cli.cmd_arg('--ssh_key', type=str, default=None)
@cli.cmd_arg('--ssh_key_file', type=str, default=None)
@cli.cmd_arg('--affiliate_amount', type=int, default=None)
@cli.cmd_arg('--affiliate_token', type=str, default=None)
def launch(vm_hostname,
days,
disk,
memory,
ipv4,
ipv6,
bandwidth,
host=None,
api_endpoint=API_ENDPOINT,
cores=1,
currency='bch',
region=None,
managed=False,
organization=None,
override_code=None,
settlement_token=None,
qemuopts=None,
hostaccess=False,
ipxescript=None,
ipxescript_stdin=False,
ipxescript_file=None,
operating_system=None,
ssh_key=None,
ssh_key_file=None,
walkingliberty_wallet=None,
want_topup=False,
save=True,
affiliate_amount=None,
affiliate_token=None):
@cli.cmd_arg("vm_hostname")
@cli.cmd_arg("--host", type=str, default=None)
@cli.cmd_arg("--hostaccess", type=bool, default=False)
@cli.cmd_arg("--save", type=bool, default=True)
@cli.cmd_arg("--override_code", type=str, default=None)
@cli.cmd_arg("--region", type=str, default=None)
@cli.cmd_arg("--managed", type=bool, default=False)
@cli.cmd_arg("--currency", type=str, default=None)
@cli.cmd_arg("--settlement_token", type=str, default=None)
@cli.cmd_arg("--cores", type=int, default=1)
@cli.cmd_arg("--memory", type=int, default=1)
@cli.cmd_arg("--bandwidth", type=int, default=1)
@cli.cmd_arg("--ipv4", default="/32")
@cli.cmd_arg("--ipv6", default="/128")
@cli.cmd_arg("--disk", type=int, default=5)
@cli.cmd_arg("--days", type=int, required=True)
@cli.cmd_arg("--qemuopts", type=str, default=None)
@cli.cmd_arg("--walkingliberty_wallet", type=str, default=None)
@cli.cmd_arg("--api_endpoint", type=str, default=API_ENDPOINT)
@cli.cmd_arg("--want_topup", type=bool, default=False)
@cli.cmd_arg("--organization", type=str, default=None)
@cli.cmd_arg("--ipxescript", type=str, default=None)
@cli.cmd_arg("--ipxescript_stdin", type=bool, default=False)
@cli.cmd_arg("--ipxescript_file", type=str, default=None)
@cli.cmd_arg("--operating_system", type=str, default=None)
@cli.cmd_arg("--ssh_key", type=str, default=None)
@cli.cmd_arg("--ssh_key_file", type=str, default=None)
@cli.cmd_arg("--affiliate_amount", type=int, default=None)
@cli.cmd_arg("--affiliate_token", type=str, default=None)
def launch(
vm_hostname,
days,
disk,
memory,
ipv4,
ipv6,
bandwidth,
host=None,
api_endpoint=API_ENDPOINT,
cores=1,
currency="bch",
region=None,
managed=False,
organization=None,
override_code=None,
settlement_token=None,
qemuopts=None,
hostaccess=False,
ipxescript=None,
ipxescript_stdin=False,
ipxescript_file=None,
operating_system=None,
ssh_key=None,
ssh_key_file=None,
walkingliberty_wallet=None,
want_topup=False,
save=True,
affiliate_amount=None,
affiliate_token=None,
):
"""
Attempts to launch a server.
"""
@ -151,17 +149,17 @@ def launch(vm_hostname,
if settlement_token is not None:
if currency is None:
currency = 'settlement'
currency = "settlement"
if machine_exists(vm_hostname):
message = '{} already created.'.format(vm_hostname)
message = "{} already created.".format(vm_hostname)
raise ValueError(message)
if host is None and api_endpoint is None:
raise ValueError('host and/or api_endpoint must be set.')
raise ValueError("host and/or api_endpoint must be set.")
if ssh_key is not None and ssh_key_file is not None:
raise ValueError('Only ssh_key or ssh_key_file can be set.')
raise ValueError("Only ssh_key or ssh_key_file can be set.")
if ssh_key_file is not None:
with open(ssh_key_file) as fp:
ssh_key = fp.read()
@ -170,7 +168,7 @@ def launch(vm_hostname,
for ipxe_option in [ipxescript, ipxescript_stdin, ipxescript_file]:
if ipxe_option not in [False, None]:
ipxe_not_none_or_false = ipxe_not_none_or_false + 1
msg = 'Only set one of ipxescript, ipxescript_stdin, ipxescript_file'
msg = "Only set one of ipxescript, ipxescript_stdin, ipxescript_file"
if ipxe_not_none_or_false > 1:
raise ValueError(msg)
if ipxescript_stdin is True:
@ -180,7 +178,7 @@ def launch(vm_hostname,
ipxescript = fp.read()
# FIXME: Hacky.
if host == '127.0.0.1':
if host == "127.0.0.1":
if walkingliberty_wallet is None and settlement_token is None:
if override_code is None:
override_code = get_override_code()
@ -189,50 +187,54 @@ def launch(vm_hostname,
def create_vm(host):
create = api_client.launch
return create(host=host,
machine_id=machine_id,
days=days,
disk=disk,
memory=memory,
ipxescript=ipxescript,
operating_system=operating_system,
ssh_key=ssh_key,
cores=cores,
ipv4=ipv4,
ipv6=ipv6,
bandwidth=bandwidth,
currency=currency,
region=region,
organization=organization,
managed=managed,
override_code=override_code,
settlement_token=settlement_token,
qemuopts=qemuopts,
hostaccess=hostaccess,
api_endpoint=api_endpoint,
want_topup=want_topup,
affiliate_amount=affiliate_amount,
affiliate_token=affiliate_token,
retry=True)
return create(
host=host,
machine_id=machine_id,
days=days,
disk=disk,
memory=memory,
ipxescript=ipxescript,
operating_system=operating_system,
ssh_key=ssh_key,
cores=cores,
ipv4=ipv4,
ipv6=ipv6,
bandwidth=bandwidth,
currency=currency,
region=region,
organization=organization,
managed=managed,
override_code=override_code,
settlement_token=settlement_token,
qemuopts=qemuopts,
hostaccess=hostaccess,
api_endpoint=api_endpoint,
want_topup=want_topup,
affiliate_amount=affiliate_amount,
affiliate_token=affiliate_token,
retry=True,
)
created_dict = create_vm(host)
logging.debug(created_dict)
if api_endpoint is not None:
# Adjust host to whatever it gives us.
host = created_dict['host']
host = created_dict["host"]
# This will be false at least the first time if paying with BTC or BCH.
if created_dict['paid'] is False:
uri = created_dict['payment']['uri']
if created_dict["paid"] is False:
uri = created_dict["payment"]["uri"]
if 'usd' in created_dict['payment']:
usd = created_dict['payment']['usd']
if "usd" in created_dict["payment"]:
usd = created_dict["payment"]["usd"]
else:
usd = None
make_payment(currency=currency,
uri=uri,
usd=usd,
walkingliberty_wallet=walkingliberty_wallet)
make_payment(
currency=currency,
uri=uri,
usd=usd,
walkingliberty_wallet=walkingliberty_wallet,
)
tries = 360
while tries > 0:
@ -243,106 +245,112 @@ def launch(vm_hostname,
time.sleep(10)
created_dict = create_vm(host)
logging.debug(created_dict)
if created_dict['paid'] is True:
if created_dict["paid"] is True:
break
if created_dict['created'] is False:
if created_dict["created"] is False:
tries = 360
while tries > 0:
logging.info('Waiting for server to build...')
logging.info("Waiting for server to build...")
tries = tries + 1
# Waiting for server to spin up.
time.sleep(10)
created_dict = create_vm(host)
logging.debug(created_dict)
if created_dict['created'] is True:
if created_dict["created"] is True:
break
if created_dict['created'] is False:
if created_dict["created"] is False:
logging.warning(created_dict)
# FIXME: Bad exception type.
raise ValueError('Server creation failed, tries exceeded.')
raise ValueError("Server creation failed, tries exceeded.")
if 'host' not in created_dict:
created_dict['host'] = host
created_dict['vm_hostname'] = vm_hostname
created_dict['machine_id'] = machine_id
created_dict['api_endpoint'] = api_endpoint
if "host" not in created_dict:
created_dict["host"] = host
created_dict["vm_hostname"] = vm_hostname
created_dict["machine_id"] = machine_id
created_dict["api_endpoint"] = api_endpoint
save_machine_info(created_dict)
print(pretty_machine_info(created_dict), file=sys.stderr)
return created_dict
@cli.cmd
@cli.cmd_arg('vm_hostname')
@cli.cmd_arg('--override_code', type=str, default=None)
@cli.cmd_arg('--currency', type=str, default=None)
@cli.cmd_arg('--settlement_token', type=str, default=None)
@cli.cmd_arg('--days', type=int, required=True)
@cli.cmd_arg('--walkingliberty_wallet', type=str, default=None)
@cli.cmd_arg('--api_endpoint', type=str, default=None)
@cli.cmd_arg('--affiliate_amount', type=int, default=None)
@cli.cmd_arg('--affiliate_token', type=str, default=None)
def topup(vm_hostname,
days,
currency,
override_code=None,
settlement_token=None,
walkingliberty_wallet=None,
api_endpoint=None,
affiliate_amount=None,
affiliate_token=None):
@cli.cmd_arg("vm_hostname")
@cli.cmd_arg("--override_code", type=str, default=None)
@cli.cmd_arg("--currency", type=str, default=None)
@cli.cmd_arg("--settlement_token", type=str, default=None)
@cli.cmd_arg("--days", type=int, required=True)
@cli.cmd_arg("--walkingliberty_wallet", type=str, default=None)
@cli.cmd_arg("--api_endpoint", type=str, default=None)
@cli.cmd_arg("--affiliate_amount", type=int, default=None)
@cli.cmd_arg("--affiliate_token", type=str, default=None)
def topup(
vm_hostname,
days,
currency,
override_code=None,
settlement_token=None,
walkingliberty_wallet=None,
api_endpoint=None,
affiliate_amount=None,
affiliate_token=None,
):
"""
tops up an existing vm.
"""
if settlement_token is not None:
if currency is None:
currency = 'settlement'
currency = "settlement"
if not machine_exists(vm_hostname):
message = '{} does not exist.'.format(vm_hostname)
message = "{} does not exist.".format(vm_hostname)
raise ValueError(message)
machine_info = get_machine_info(vm_hostname)
machine_id = machine_info['machine_id']
machine_id = machine_info["machine_id"]
# hostname of the host
host = machine_info['host']
host = machine_info["host"]
if api_endpoint is None:
api_endpoint = machine_info['api_endpoint']
api_endpoint = machine_info["api_endpoint"]
if api_endpoint is None:
# FIXME: Hacky.
if host == '127.0.0.1':
if host == "127.0.0.1":
if walkingliberty_wallet is None and settlement_token is None:
if override_code is None:
override_code = get_override_code()
def topup_vm():
return api_client.topup(host=host,
machine_id=machine_id,
days=days,
currency=currency,
override_code=override_code,
api_endpoint=api_endpoint,
settlement_token=settlement_token,
affiliate_amount=affiliate_amount,
affiliate_token=affiliate_token,
retry=True)
return api_client.topup(
host=host,
machine_id=machine_id,
days=days,
currency=currency,
override_code=override_code,
api_endpoint=api_endpoint,
settlement_token=settlement_token,
affiliate_amount=affiliate_amount,
affiliate_token=affiliate_token,
retry=True,
)
topped_dict = topup_vm()
# This will be false at least the first time if paying with anything
# but settlement.
if topped_dict['paid'] is False:
uri = topped_dict['payment']['uri']
if topped_dict["paid"] is False:
uri = topped_dict["payment"]["uri"]
if 'usd' in topped_dict['payment']:
usd = topped_dict['payment']['usd']
if "usd" in topped_dict["payment"]:
usd = topped_dict["payment"]["usd"]
else:
usd = None
make_payment(currency=currency,
uri=uri,
usd=usd,
walkingliberty_wallet=walkingliberty_wallet)
make_payment(
currency=currency,
uri=uri,
usd=usd,
walkingliberty_wallet=walkingliberty_wallet,
)
tries = 360
while tries > 0:
@ -352,19 +360,19 @@ def topup(vm_hostname,
# Waiting for payment to set in.
time.sleep(10)
topped_dict = topup_vm()
if topped_dict['paid'] is True:
if topped_dict["paid"] is True:
break
machine_info['expiration'] = topped_dict['expiration']
machine_info["expiration"] = topped_dict["expiration"]
save_machine_info(machine_info, overwrite=True)
return machine_info['expiration']
return machine_info["expiration"]
def machine_info_directory():
if i_am_root():
directory = '/etc/sporestackv2'
directory = "/etc/sporestackv2"
else:
directory = os.path.join(os.getenv('HOME'), '.sporestackv2')
directory = os.path.join(os.getenv("HOME"), ".sporestackv2")
return directory
@ -373,15 +381,15 @@ def save_machine_info(machine_info, overwrite=False):
Save info to disk.
"""
if overwrite is True:
mode = 'w'
mode = "w"
else:
mode = 'x'
mode = "x"
os.umask(0o0077)
directory = machine_info_directory()
if not os.path.exists(directory):
os.mkdir(directory)
vm_hostname = machine_info['vm_hostname']
json_path = os.path.join(directory, '{}.json'.format(vm_hostname))
vm_hostname = machine_info["vm_hostname"]
json_path = os.path.join(directory, "{}.json".format(vm_hostname))
with open(json_path, mode) as json_file:
json.dump(machine_info, json_file)
return True
@ -393,39 +401,38 @@ def get_machine_info(vm_hostname):
"""
directory = machine_info_directory()
if not machine_exists(vm_hostname):
msg = '{} does not exist in {}'.format(vm_hostname, directory)
msg = "{} does not exist in {}".format(vm_hostname, directory)
raise ValueError(msg)
json_path = os.path.join(directory, '{}.json'.format(vm_hostname))
json_path = os.path.join(directory, "{}.json".format(vm_hostname))
with open(json_path) as json_file:
machine_info = json.load(json_file)
if machine_info['vm_hostname'] != vm_hostname:
raise ValueError('vm_hostname does not match filename.')
if machine_info["vm_hostname"] != vm_hostname:
raise ValueError("vm_hostname does not match filename.")
return machine_info
def pretty_machine_info(info):
msg = 'Hostname: {}\n'.format(info['vm_hostname'])
msg += 'Machine ID (keep this secret!): {}\n'.format(info['machine_id'])
if info['network_interfaces'][0] == {}:
msg += 'SSH hostname: {}\n'.format(info['sshhostname'])
msg = "Hostname: {}\n".format(info["vm_hostname"])
msg += "Machine ID (keep this secret!): {}\n".format(info["machine_id"])
if info["network_interfaces"][0] == {}:
msg += "SSH hostname: {}\n".format(info["sshhostname"])
else:
if 'ipv6' in info['network_interfaces'][0]:
msg += 'IPv6: {}\n'.format(info['network_interfaces'][0]['ipv6'])
if 'ipv4' in info['network_interfaces'][0]:
msg += 'IPv4: {}\n'.format(info['network_interfaces'][0]['ipv4'])
if "ipv6" in info["network_interfaces"][0]:
msg += "IPv6: {}\n".format(info["network_interfaces"][0]["ipv6"])
if "ipv4" in info["network_interfaces"][0]:
msg += "IPv4: {}\n".format(info["network_interfaces"][0]["ipv4"])
expiration = info["expiration"]
human_expiration = time.strftime('%Y-%m-%d %H:%M:%S %z',
time.localtime(expiration))
if 'running' in info:
msg += 'Running: {}\n'.format(info['running'])
msg += 'Expiration: {} ({})\n'.format(expiration, human_expiration)
human_expiration = time.strftime("%Y-%m-%d %H:%M:%S %z", time.localtime(expiration))
if "running" in info:
msg += "Running: {}\n".format(info["running"])
msg += "Expiration: {} ({})\n".format(expiration, human_expiration)
time_to_live = expiration - int(time.time())
if time_to_live > 0:
hours = time_to_live // 3600
msg += 'Server will be deleted in {} hours.'.format(hours)
msg += "Server will be deleted in {} hours.".format(hours)
else:
hours = time_to_live * -1 // 3600
msg += 'Server deleted {} hours ago.'.format(hours)
msg += "Server deleted {} hours ago.".format(hours)
return msg
@ -437,13 +444,15 @@ def list():
directory = machine_info_directory()
infos = []
for vm_hostname_json in os.listdir(directory):
vm_hostname = vm_hostname_json.split('.')[0]
vm_hostname = vm_hostname_json.split(".")[0]
saved_vm_info = get_machine_info(vm_hostname)
upstream_vm_info = api_client.info(host=saved_vm_info['host'],
machine_id=saved_vm_info['machine_id'],
api_endpoint=saved_vm_info['api_endpoint'])
saved_vm_info['expiration'] = upstream_vm_info['expiration']
saved_vm_info['running'] = upstream_vm_info['running']
upstream_vm_info = api_client.info(
host=saved_vm_info["host"],
machine_id=saved_vm_info["machine_id"],
api_endpoint=saved_vm_info["api_endpoint"],
)
saved_vm_info["expiration"] = upstream_vm_info["expiration"]
saved_vm_info["running"] = upstream_vm_info["running"]
infos.append(saved_vm_info)
for info in infos:
@ -461,14 +470,14 @@ def remove(vm_hostname):
os.remove(machine_info_directory() + "/" + vm_hostname + ".json")
@cli.cmd(name='remove')
@cli.cmd_arg('vm_hostname')
@cli.cmd(name="remove")
@cli.cmd_arg("vm_hostname")
def remove_cli(vm_hostname):
info = get_machine_info(vm_hostname)
print(info)
print(pretty_machine_info(info))
remove(vm_hostname)
print('{} removed.'.format(vm_hostname))
print("{} removed.".format(vm_hostname))
def machine_exists(vm_hostname):
@ -476,7 +485,7 @@ def machine_exists(vm_hostname):
Check if the VM exists locally in /etc/sporestackv2 or ~/.sporestackv2
"""
directory = machine_info_directory()
file_path = os.path.join(directory, '{}.json'.format(vm_hostname))
file_path = os.path.join(directory, "{}.json".format(vm_hostname))
if os.path.exists(file_path):
return True
else:
@ -484,8 +493,8 @@ def machine_exists(vm_hostname):
@cli.cmd
@cli.cmd_arg('vm_hostname')
@cli.cmd_arg('attribute')
@cli.cmd_arg("vm_hostname")
@cli.cmd_arg("attribute")
def get_attribute(vm_hostname, attribute):
"""
Returns an attribute about the VM.
@ -500,120 +509,113 @@ def get_override_code():
localhost spawns.
"""
try:
with open('/etc/vmmanagement.override_code') as fp:
override_code = fp.read().strip('\n')
with open("/etc/vmmanagement.override_code") as fp:
override_code = fp.read().strip("\n")
except Exception:
override_code = None
return override_code
@cli.cmd
@cli.cmd_arg('vm_hostname')
@cli.cmd_arg('--api_endpoint', type=str, default=None)
@cli.cmd_arg("vm_hostname")
@cli.cmd_arg("--api_endpoint", type=str, default=None)
def info(vm_hostname, api_endpoint=None):
"""
Info on the VM
"""
machine_info = get_machine_info(vm_hostname)
host = machine_info['host']
machine_id = machine_info['machine_id']
host = machine_info["host"]
machine_id = machine_info["machine_id"]
if api_endpoint is None:
api_endpoint = machine_info['api_endpoint']
return api_client.info(host=host,
machine_id=machine_id,
api_endpoint=api_endpoint)
api_endpoint = machine_info["api_endpoint"]
return api_client.info(host=host, machine_id=machine_id, api_endpoint=api_endpoint)
@cli.cmd
@cli.cmd_arg('vm_hostname')
@cli.cmd_arg('--api_endpoint', type=str, default=None)
@cli.cmd_arg("vm_hostname")
@cli.cmd_arg("--api_endpoint", type=str, default=None)
def start(vm_hostname, api_endpoint=None):
"""
Boots the VM.
"""
machine_info = get_machine_info(vm_hostname)
host = machine_info['host']
machine_id = machine_info['machine_id']
host = machine_info["host"]
machine_id = machine_info["machine_id"]
if api_endpoint is None:
api_endpoint = machine_info['api_endpoint']
return api_client.start(host=host,
machine_id=machine_id,
api_endpoint=api_endpoint)
api_endpoint = machine_info["api_endpoint"]
return api_client.start(host=host, machine_id=machine_id, api_endpoint=api_endpoint)
@cli.cmd
@cli.cmd_arg('vm_hostname')
@cli.cmd_arg('--api_endpoint', type=str, default=None)
@cli.cmd_arg("vm_hostname")
@cli.cmd_arg("--api_endpoint", type=str, default=None)
def stop(vm_hostname, api_endpoint=None):
"""
Immediately kills the VM.
"""
machine_info = get_machine_info(vm_hostname)
host = machine_info['host']
machine_id = machine_info['machine_id']
host = machine_info["host"]
machine_id = machine_info["machine_id"]
if api_endpoint is None:
api_endpoint = machine_info['api_endpoint']
return api_client.stop(host=host,
machine_id=machine_id,
api_endpoint=api_endpoint)
api_endpoint = machine_info["api_endpoint"]
return api_client.stop(host=host, machine_id=machine_id, api_endpoint=api_endpoint)
@cli.cmd
@cli.cmd_arg('vm_hostname')
@cli.cmd_arg('--api_endpoint', type=str, default=None)
@cli.cmd_arg("vm_hostname")
@cli.cmd_arg("--api_endpoint", type=str, default=None)
def delete(vm_hostname, api_endpoint=None):
"""
Deletes the VM (most likely prematurely.
"""
machine_info = get_machine_info(vm_hostname)
host = machine_info['host']
machine_id = machine_info['machine_id']
host = machine_info["host"]
machine_id = machine_info["machine_id"]
if api_endpoint is None:
api_endpoint = machine_info['api_endpoint']
api_client.delete(host=host,
machine_id=machine_id,
api_endpoint=api_endpoint)
api_endpoint = machine_info["api_endpoint"]
api_client.delete(host=host, machine_id=machine_id, api_endpoint=api_endpoint)
# Also remove the .json file
remove(vm_hostname)
@cli.cmd
@cli.cmd_arg('vm_hostname')
@cli.cmd_arg('--api_endpoint', type=str, default=None)