Browse Source

sporestack-python: 0.1.0

tags/0.1.1
Teran McKinney 3 years ago
commit
4ef1c6efe8
14 changed files with 649 additions and 0 deletions
  1. +6
    -0
      .gitignore
  2. +24
    -0
      LICENSE.txt
  3. +70
    -0
      README.md
  4. +73
    -0
      examples/bitcoinunlimited.py
  5. +76
    -0
      examples/bitcoinunlimited.sh
  6. +66
    -0
      examples/torrelay.py
  7. +74
    -0
      examples/torrelay.sh
  8. +2
    -0
      requirements.txt
  9. +2
    -0
      setup.cfg
  10. +32
    -0
      setup.py
  11. +87
    -0
      sporestack/__init__.py
  12. +97
    -0
      sporestack/nodemeup.py
  13. +35
    -0
      test.py
  14. +5
    -0
      test.sh

+ 6
- 0
.gitignore View File

@@ -0,0 +1,6 @@
*.pyc
venv
build
dist
*.egg-info
.eggs

+ 24
- 0
LICENSE.txt View File

@@ -0,0 +1,24 @@
This is free and unencumbered software released into the public domain.

Anyone is free to copy, modify, publish, use, compile, sell, or
distribute this software, either in source code form or as a compiled
binary, for any purpose, commercial or non-commercial, and by any
means.

In jurisdictions that recognize copyright laws, the author or authors
of this software dedicate any and all copyright interest in the
software to the public domain. We make this dedication for the benefit
of the public at large and to the detriment of our heirs and
successors. We intend this dedication to be an overt act of
relinquishment in perpetuity of all present and future rights to this
software under copyright law.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.

For more information, please refer to <http://unlicense.org/>

+ 70
- 0
README.md View File

@@ -0,0 +1,70 @@
Python library for http://sporestack.com/

# Installation

* `pip install sporestack`

# Usage

Spawn one from your terminal.

```
import sporestack
from uuid import uuid4 as random_uuid

node_uuid = str(random_uuid())

ssh_key_path = '{}/.ssh/id_rsa.pub'.format(os.getenv('HOME'))

with open(ssh_key_path) as ssh_key_file:
sshkey = ssh_key_file.read()

while True:
node = sporestack.node(days=28,
sshkey=sshkey,
unique=node_uuid)
if node.payment_status is False:
amount = "{0:.8f}".format(node.satoshis *
0.00000001)
uri = 'bitcoin:{}?amount={}'.format(node.address, amount)
qr = pyqrcode.create(uri)
print(qr.terminal())
print(uri)
print('Pay with Bitcoin. Resize your terminal if QR code is unclear.')
else:
print('Node being built...')
if node.creation_status is True:
break
sleep(5)

banner = '''

UUID: {}
IPv6: {}
IPv4: {}
End of Life: {}

May take a few more moments to come online.

'''.format(node_uuid,
node.ip6,
node.ip4,
node.end_of_life)

print(banner)

```

Spawn one and SSH into it.

```
nodemeup
```

# Examples

* [Launch a tor relay](examples/torrelay.py)

# Licence

[Unlicense/Public domain](LICENSE.txt)

+ 73
- 0
examples/bitcoinunlimited.py View File

@@ -0,0 +1,73 @@
"""
Creates a 28 day Bitcoin Unlimited node in Dallas.

Doesn't work.
"""

from uuid import uuid4 as random_uuid
from time import sleep
import os

import pyqrcode
import sporestack

# 1 - 28
DAYS = 28

# 3 is DFW.
DCID = 3

# FreeBSD 11. Will need to change the startupscript if you adjust this.
OSID = 230

# 4GiB memory, 90GiB disk
FLAVOR=95

node_uuid = str(random_uuid())

ssh_key_path = '{}/.ssh/id_rsa.pub'.format(os.getenv('HOME'))

with open(ssh_key_path) as ssh_key_file:
sshkey = ssh_key_file.read()

with open('bitcoinunlimited.sh') as startup_file:
startupscript = startup_file.read()

# This is broken.
while False:
node = sporestack.node(days=28,
sshkey=sshkey,
startupscript=startupscript,
unique=node_uuid,
flavor=FLAVOR,
osid=OSID,
dcid=DCID)
if node.payment_status is False:
amount = "{0:.8f}".format(node.satoshis *
0.00000001)
uri = 'bitcoin:{}?amount={}'.format(node.address, amount)
qr = pyqrcode.create(uri)
print(qr.terminal())
print(uri)
print('Pay with Bitcoin. Resize your terminal if QR code is unclear.')
else:
print('Node being built...')
if node.creation_status is True:
break
sleep(5)

banner = '''

UUID: {}
IPv6: {}
IPv4: {}
End of Life: {}

May take a few more moments to come online.

'''.format(node_uuid,
node.ip6,
node.ip4,
node.end_of_life)

print(banner)

+ 76
- 0
examples/bitcoinunlimited.sh View File

@@ -0,0 +1,76 @@
#!/bin/sh

# Stops/breaks at:
# In file included from leveldb/db/builder.cc:7:
# In file included from ./leveldb/db/filename.h:14:
# In file included from ./leveldb/port/port.h:14:
# ./leveldb/port/port_posix.h:38:12: fatal error: 'endian.h' file not found
# #include <endian.h>
# ^
# 1 warning and 1 error generated.
# gmake[2]: *** [Makefile:3874: leveldb/db/leveldb_libleveldb_a-builder.o] Error 1
#

progress() {
echo "bitcoinunlimited: $*" > /dev/console
echo "bitcoinunlimited: $*"
}

# This runs at the top of cloud-init. We don't even have SSHD running without
# this.

# <- Does it still do that?

export ASSUME_ALWAYS_YES=yes

export PATH=/sbin:/bin:/usr/sbin:/usr/bin:/usr/local/sbin:/usr/local/bin

progress 'Starting pkg upgrade'
pkg upgrade

progress 'Starting pkg install'
pkg upgrade
pkg install ca_root_nss autotools pkgconf gmake boost-libs libevent2 openssl db48 git micro_httpd micro_inetd #FIXME TODO: Remove git
chmod 700 /root
mkdir /root/.bitcoin
echo 'rpcuser=bitcoinunlimited
rpcpassword=bitcoinunlimitedpassword' > /root/.bitcoin/bitcoin.conf
# ^ If the user and password are the same, it will fail.

mkdir /root/BitcoinUnlimited

# tmpfs for speed and because / is too small otherwise.
# growfs seems to have problems, not sure why.
mount -t tmpfs tmpfs /root/BitcoinUnlimited
cd /root/BitcoinUnlimited
#FIXME This seems to have a 302 loop right now.
#fetch -qo - https://github.com/bitcoinunlimited/bitcoinunlimited/archive/$TAG.tar.gz | tar xzf -

progress 'Starting git clone'
git clone --depth 1 https://github.com/BitcoinUnlimited/BitcoinUnlimited.git bu-src
cd BitcoinUnlimited
./autogen.sh
./configure --with-gui=no --without-miniupnpc --disable-wallet
progress 'About to compile'
gmake -j 2
gmake install
cd /
umount /root/BitcoinUnlimited

fetch -q \
https://raw.githubusercontent.com/teran-mckinney/bitnoder/master/fs/etc/rc.local \
-o /etc/rc.local

fetch -q \
https://raw.githubusercontent.com/teran-mckinney/bitnoder/master/fs/usr/local/bin/honeybadgermoneystats \
-o /usr/local/bin/honeybadgermoneystats

echo > /usr/local/etc/bitnoder.conf

echo 'ntpd_enable="YES"' >> /etc/rc.conf

chmod 500 /etc/rc.local
chmod 500 /usr/local/bin/honeybadgermoneystats

# Let the boot process start rc.local on its own.
#/etc/rc.local

+ 66
- 0
examples/torrelay.py View File

@@ -0,0 +1,66 @@
"""
Creates a 28 day tor relay in Dallas.
"""

from uuid import uuid4 as random_uuid
from time import sleep
import os

import pyqrcode
import sporestack

# 1 - 28
DAYS = 28

# 3 is DFW.
DCID = 3

# FreeBSD 11. Will need to change the startupscript if you adjust this.
OSID = 230

node_uuid = str(random_uuid())

ssh_key_path = '{}/.ssh/id_rsa.pub'.format(os.getenv('HOME'))

with open(ssh_key_path) as ssh_key_file:
sshkey = ssh_key_file.read()

with open('torrelay.sh') as startup_file:
startupscript = startup_file.read()

while True:
node = sporestack.node(days=28,
sshkey=sshkey,
startupscript=startupscript,
unique=node_uuid,
osid=OSID,
dcid=DCID)
if node.payment_status is False:
amount = "{0:.8f}".format(node.satoshis *
0.00000001)
uri = 'bitcoin:{}?amount={}'.format(node.address, amount)
qr = pyqrcode.create(uri)
print(qr.terminal())
print(uri)
print('Pay with Bitcoin. Resize your terminal if QR code is unclear.')
else:
print('Node being built...')
if node.creation_status is True:
break
sleep(5)

banner = '''

UUID: {}
IPv6: {}
IPv4: {}
End of Life: {}

May take a few more moments to come online.

'''.format(node_uuid,
node.ip6,
node.ip4,
node.end_of_life)

print(banner)

+ 74
- 0
examples/torrelay.sh View File

@@ -0,0 +1,74 @@
#!/bin/sh

progress() {
echo "$NAME: $*" > /dev/console
echo "$NAME: $*"
}

# This runs at the top of cloud-init. We don't even have SSHD running without
# this.

export ASSUME_ALWAYS_YES=yes

export PATH=/sbin:/bin:/usr/sbin:/usr/bin:/usr/local/sbin:/usr/local/bin

# pkg isn't installed by default on vultr, but this will bootstrap it
# with the above option of ASSUME_ALWAYS_YES=yes

progress 'Starting pkg upgrade'
pkg upgrade

progress 'Starting pkg install'
pkg upgrade
pkg install tor

sysctl net.inet.ip.random_id=1
echo 'net.inet.ip.random_id=1' >> /etc/sysctl.conf

# May need to consider bandwidth allowances with the plan and how high the
# rate limit is. This is 2.6TiB theoretical max, but probably would be a little
# higher in one month.

# IPv6 global address has to be specified manually.
# We also may not have it unless we probe for it explictly.

echo 'ifconfig_vtnet0_ipv6="inet6 accept_rtadv"
rtsold_enable=YES
ipv6_activate_all_interfaces=YES
dumpdev="NO"
moused_enable="NO"
sendmail_enable="NONE"
ip6addrctl_policy="ipv6_prefer"' >> /etc/rc.conf

# This is rather ugly, I'm sorry.
ifconfig vtnet0 inet6 auto_linklocal
ifconfig vtnet0 inet6 accept_rtadv
ifconfig vtnet0 inet6 -ifdisabled

service rtsold start
rtsold -fd1 vtnet0
sleep 10
rtsold -fd1 vtnet0
IPV6="$(ifconfig vtnet0 | grep inet6 | grep -v 'inet6 fe80' | awk '{print $2}')"
if [ -n "$IPV6" ]; then
echo "ORPort [$IPV6]:443" > /usr/local/etc/tor/torrc
fi
echo 'ORPort 443
Nickname BuiltAutomatically
RelayBandwidthRate 1024 KB
RelayBandwidthBurst 1024 KB
ContactInfo IThinkIWasBuiltAutomatically
ExitPolicy reject *:*
ExitPolicy reject6 *:*' >> /usr/local/etc/tor/torrc


# Running tor as root, partly for port 443 use. Since this server hopefully
# only runs tor, it's safe to do.
echo 'ntpd_enable="YES"
tor_enable="YES"
tor_user="root"' >> /etc/rc.conf

chown 0:0 /var/db/tor

service ntpd start
service tor start

+ 2
- 0
requirements.txt View File

@@ -0,0 +1,2 @@
pyyaml
pyqrcode

+ 2
- 0
setup.cfg View File

@@ -0,0 +1,2 @@
[metadata]
description-file = README.md

+ 32
- 0
setup.py View File

@@ -0,0 +1,32 @@
#!/usr/bin/env python

from setuptools import setup

VERSION = '0.1.0'

DOWNLOAD_URL = 'https://github.com/sporestack/sporestack-python/tarball/{}'

setup(
name='sporestack',
version=VERSION,
author='Teran McKinney',
author_email='sega01@go-beyond.org',
description='Create servers for Bitcoin. sporestack.com library.',
keywords=['bitcoin', 'servers', 'infrastructure'],
license='Unlicense',
url='http://sporestack.com/',
download_url=DOWNLOAD_URL.format(VERSION),
packages=['sporestack'],
setup_requires=[
'flake8'
],
install_requires=[
'pyyaml',
'pyqrcode'
],
entry_points={
'console_scripts': [
'nodemeup = sporestack.nodemeup:fakemain'
]
}
)

+ 87
- 0
sporestack/__init__.py View File

@@ -0,0 +1,87 @@
"""
sporestack.com Python API interface

Released into the public domain.
"""

from collections import namedtuple
from warnings import warn
from base64 import b64encode
import json
import urllib2

import yaml


ENDPOINT = 'http://sporestack.com/node'

# ENDPOINT = 'http://localhost:8082/node'

TIMEOUT = 30


def node(days,
unique,
sshkey=None,
cloudinit=None,
startupscript=None,
osid=None,
dcid=None,
flavor=None,
endpoint=ENDPOINT):
"""
Returns a node

Returns:
node.payment_status
node.end_of_life
node.ip6
node.ip4
"""

pre_data = {'days': days,
'unique': unique}

# There must be a better way to do this...
if cloudinit is not None:
b64_cloudinit = b64encode(cloudinit)
pre_data['cloudinit'] = b64_cloudinit
if sshkey is not None:
pre_data['sshkey'] = sshkey
if startupscript is not None:
pre_data['startupscript'] = startupscript
if osid is not None:
pre_data['osid'] = osid
if dcid is not None:
pre_data['dcid'] = dcid
if flavor is not None:
pre_data['flavor'] = flavor

post_data = json.dumps(pre_data)
try:
http_return = urllib2.urlopen(endpoint,
data=post_data,
timeout=TIMEOUT)
except urllib2.HTTPError as http_error:
if http_error.code == 400:
# Throw exception with output from endpoint..
raise Exception(http_error.read())
else:
raise
if http_return.getcode() == 200:
data = yaml.safe_load(http_return.read())
if 'deprecated' in data:
if data['deprecated'] is not False:
warn(str(data['deprecated']), DeprecationWarning)
node = namedtuple('node',
data.keys())
node.end_of_life = data['end_of_life']
node.payment_status = data['payment_status']
node.creation_status = data['creation_status']
node.address = data['address']
node.satoshis = data['satoshis']
node.ip4 = data['ip4']
node.ip6 = data['ip6']
return node
else:
raise Exception('Fatal issue with sporestack.')

+ 97
- 0
sporestack/nodemeup.py View File

@@ -0,0 +1,97 @@
"""
Build a server and SSH into it. Server will last for a day.
"""

import argparse
from uuid import uuid4 as random_uuid
from time import sleep
import os
from socket import create_connection

import pyqrcode
import sporestack

node_uuid = str(random_uuid())

ssh_key_path = '{}/.ssh/id_rsa.pub'.format(os.getenv('HOME'))

with open(ssh_key_path) as ssh_key_file:
sshkey = ssh_key_file.read()

parser = argparse.ArgumentParser()

parser.add_argument('--osid', help='Operating System ID '
'230: FreeBSD 11, 215: Ubuntu 16.04, '
'179: CoreOS Stable, 193: Debian 8, '
'167: CentOS 7',
type=int)
parser.add_argument('--dcid', help='Datacenter ID '
'3: Dallas, 2: Chicago, '
'12: San Jose, 5: Los Angeles, '
'6: Atlanta, 1: New Jersey, '
'39: Miami, 4: Seattle',
type=int)

args = parser.parse_args()

while True:
node = sporestack.node(days=1,
sshkey=sshkey,
unique=node_uuid,
osid=args.osid,
dcid=args.dcid)
if node.payment_status is False:
amount = "{0:.8f}".format(node.satoshis *
0.00000001)
uri = 'bitcoin:{}?amount={}'.format(node.address, amount)
qr = pyqrcode.create(uri)
print(qr.terminal())
print(uri)
print('Pay with Bitcoin. Resize your terminal if QR code is unclear.')
else:
print('Node being built...')
if node.creation_status is True:
break
sleep(5)

print('Waiting for node to come online.')
ipaddress = None
while True:
for ip in [node.ip6, node.ip4]:
try:
socket = create_connection((ip, 22), timeout=2)
socket.close()
ipaddress = ip
break
except:
print('Waiting for node to come online.')
sleep(2)
if ipaddress is not None:
break

banner = '''

UUID: {}
IPv6: {}
IPv4: {}
End of Life: {}

'''.format(node_uuid,
node.ip6,
node.ip4,
node.end_of_life)

print(banner)

command = ('ssh root@{} -p 22 -oStrictHostKeyChecking=no'
' -oUserKnownHostsFile=/dev/null'.format(ipaddress))
os.system(command)

print(banner)

def fakemain():
'''
NOOP
I need to fix this.
'''
a = 0

+ 35
- 0
test.py View File

@@ -0,0 +1,35 @@
"""
Just some basic tests.

Kinda broken.
"""

from uuid import uuid4 as random_uuid
import os

import sporestack

script = """#!/bin/sh
date > /date
"""
new_uuid = str(random_uuid())

ssh_key_path = '{}/.ssh/id_rsa.pub'.format(os.getenv('HOME'))

with open(ssh_key_path) as ssh_key_file:
sshkey = ssh_key_file.read()

for unique in ['3b69d7c9-ad4d-4d31-b04e-b224f02de4d4',
'4a79a54b-b763-4a52-9d1e-d0d72ec7f67d',
new_uuid]:
node = sporestack.node(days=1,
sshkey=sshkey,
unique=unique,
cloudinit=script,
startupscript=script)
print('Payment status: ' + str(node.payment_status))
print('Creation status: ' + str(node.creation_status))
print(node.address)
print(node.satoshis)
print(node.ip6)
print(node.ip4)

+ 5
- 0
test.sh View File

@@ -0,0 +1,5 @@
#!/bin/sh

set -e

python -W all -W error coinfee_test.py

Loading…
Cancel
Save