def virtual_machine_exists(virtual_machine):
    """
    Returns True/False depending if it exists or not.
    """
    try:
        hedron.virtual_machine_info(virtual_machine)
    except Exception:
        return False
    return True
Exemple #2
0
def network_argument(vm):
    """
    Returns the network arguments that qemu will run with, if any.
    """
    vm_info = hedron.virtual_machine_info(vm)
    if vm_info['ipv4'] is False and vm_info['ipv6'] is False:
        return []

    hostaccessargument = ''
    if vm_info['hostaccess'] is True:
        hostaccessargument = ',guestfwd=tcp:10.0.2.1:1-tcp:127.0.0.1:22'

    if vm_info['ipv4'] in ['nat', 'tor'] and vm_info['ipv6'] in ['nat', 'tor']:
        sshport = vm_info['slot']
        network_return = ['-net', 'nic,model=virtio', '-net']
        args = 'user,hostfwd=tcp:127.0.0.1:{}-:22{}'
        network_return.append(args.format(sshport, hostaccessargument))

    elif vm_info['ipv4'] == '/32' or vm_info['ipv6'] == '/128':
        mac = vm_info['network_interfaces'][0]['mac']
        interface_name = 'slot{}'.format(vm_info['slot'])
        network_return = [
            '-device', 'virtio-net-pci,netdev='
            'primary,mac={}'.format(mac), '-netdev',
            'tap,id=primary,br=primary,'
            'ifname={}'.format(interface_name)
        ]

    else:
        raise ValueError('Unsupported network argument combination.')
    return network_return
Exemple #3
0
def qemu_arguments(vm):
    vm_info = hedron.virtual_machine_info(vm)
    arguments = []
    arguments.append('-name')
    arguments.append(vm)
    arguments.append('-smp')
    arguments.append(str(vm_info['cores']))
    arguments.append('-m')
    arguments.append('{}G'.format(vm_info['memory']))
    arguments.append('-cpu')
    arguments.append('kvm64')
    arguments.append('-enable-kvm')
    arguments.append('-nodefaults')
    arguments.append('-monitor')
    arguments.append('none')
    arguments.append('-qmp')
    arguments.append('unix:/run/runqemu@{}/qmp,server,nowait'.format(vm))
    arguments.extend(qemuopts_argument(vm))
    drive = drive_argument(vm)
    if drive is not None:
        arguments.append('-drive')
        arguments.append(drive)
    arguments.append('-serial')
    arguments.append(serial_argument(vm))
    arguments.extend(network_argument(vm))
    arguments.extend(ipxe_iso_argument(vm))
    arguments.extend(boot_order_argument(vm))
    return arguments
Exemple #4
0
def drive_argument(vm):
    vm_info = hedron.virtual_machine_info(vm)
    qcow2_path = '/var/tmp/runqemu/{}/disk.qcow2'.format(vm)
    drive_argument = DRIVE.format(qcow2_path)
    if vm_info['disk'] != 0:
        return drive_argument
    else:
        return None
Exemple #5
0
def ndp_proxy(method, vm):
    validate_method(method)
    vm_info = hedron.virtual_machine_info(vm)
    ip6 = vm_info['network_interfaces'][0]['ipv6']
    device = get_default_v6_route_device()

    sh.ip('-6', 'neigh', method, 'proxy', ip6, 'dev', device)
    return True
def is_slot_available(slot):
    """
    Determines if a slot is available.
    """
    for vm in hedron.list_virtual_machines():
        if hedron.virtual_machine_info(vm)['slot'] == slot:
            return False
    return True
def list_ipv4s_in_use():
    in_use = []
    for vm in hedron.list_virtual_machines():
        vm_info = hedron.virtual_machine_info(vm)
        if 'network_interfaces' in vm_info:
            if 'ipv4' in vm_info['network_interfaces'][0]:
                in_use.append(vm_info['network_interfaces'][0]['ipv4'])
    return in_use
Exemple #8
0
def qemuopts_argument(vm):
    """
    qemuopts is a string, not a list of options.
    """
    vm_info = hedron.virtual_machine_info(vm)
    if vm_info['qemuopts'] is not None:
        return vm_info['qemuopts'].split()
    else:
        return DEFAULT_QEMUOPTS.split()
def dhcpd_config():
    head = dhcpd_head()
    body = head
    for vm in hedron.list_virtual_machines():
        vm_info = hedron.virtual_machine_info(vm)
        if 'network_interfaces' in vm_info:
            if 'ipv4' in vm_info['network_interfaces'][0]:
                mac = vm_info['network_interfaces'][0]['mac']
                ipv4 = vm_info['network_interfaces'][0]['ipv4']
                body = body + '\n' + dhcpd_snippet(vm, mac, ipv4)
    return body
Exemple #10
0
def get_gid(vm):
    """
    Logic filtering possible ipv4 and ipv6 options happens above this point.
    """
    vm_info = hedron.virtual_machine_info(vm)
    # If we are using tor, map to the appropriate tornet process.
    if vm_info['ipv4'] == 'tor' or vm_info['ipv6'] == 'tor':
        return vm_info['slot']
    else:
        # 1194 is "openvpn" which is not ran through tor.
        return 1194
Exemple #11
0
def route(method, vm):
    """
    Not needed if using primary is bridged with the public interface,
    but doesn't hurt, either.
    """
    validate_method(method)
    vm_info = hedron.virtual_machine_info(vm)
    ip6 = vm_info['network_interfaces'][0]['ipv6']
    arguments = _route_command(method, ip6)
    sh.ip(arguments)
    return True
Exemple #12
0
def topup_vm_and_wait(topup_data):
    topup_vm(topup_data)
    machine_id = topup_data['machine_id']
    expiration = topup_data['expiration']
    # Up to 30 seconds for expiration to be updated.
    for tries in range(1, 30 + 1):
        data = hedron.virtual_machine_info(machine_id)
        if data['expiration'] == expiration:
            return True
        else:
            time.sleep(1)
    logging.critical('VM topup failed for {}'.format(machine_id))
    raise ValueError('Fatal error, VM topup failed.')
Exemple #13
0
def ebtables_arguments(vm, up=True):
    """
    Returns the network arguments that qemu will run with, if any.

    For now, this primarily just filters mac address on output
    and ethertypes on both.
    """
    vm_info = hedron.virtual_machine_info(vm)

    mac = vm_info['network_interfaces'][0]['mac']
    interface = 'slot{}'.format(vm_info['slot'])

    # Insert if we are bringing the interface up, delete if bringing it down.
    if up is True:
        mode = '-A'
    else:
        mode = '-D'

    # FIXME: Too much redundancy
    # First is IPv4 and IPv6, second is IPv6 only.
    # --concurrent helps keep things stable. Can get weird rule ordering,
    # maybe duplicates without.
    if vm_info['ipv4'] == '/32':
        ebtables = ((mode, 'FORWARD', '-i', interface, '-s', mac, '-p', 'IPv4',
                     '-j', 'ACCEPT', '--concurrent'),
                    (mode, 'FORWARD', '-i', interface, '-s', mac, '-p', 'IPv6',
                     '-j', 'ACCEPT', '--concurrent'),
                    (mode, 'FORWARD', '-i', interface, '-s', mac, '-p', 'ARP',
                     '-j', 'ACCEPT', '--concurrent'),
                    (mode, 'FORWARD', '-i', interface, '-j', 'DROP',
                     '--concurrent'), (mode, 'OUTPUT', '-o', interface, '-p',
                                       'IPv4', '-j', 'ACCEPT', '--concurrent'),
                    (mode, 'OUTPUT', '-o', interface, '-p', 'IPv6', '-j',
                     'ACCEPT', '--concurrent'),
                    (mode, 'OUTPUT', '-o', interface, '-p', 'ARP', '-j',
                     'ACCEPT', '--concurrent'), (mode, 'OUTPUT', '-o',
                                                 interface, '-j', 'DROP',
                                                 '--concurrent'))
    else:
        ebtables = ((mode, 'FORWARD', '-i', interface, '-s', mac, '-p', 'IPv6',
                     '-j', 'ACCEPT', '--concurrent'),
                    (mode, 'FORWARD', '-i', interface, '-j', 'DROP',
                     '--concurrent'), (mode, 'OUTPUT', '-o', interface, '-p',
                                       'IPv6', '-j', 'ACCEPT', '--concurrent'),
                    (mode, 'OUTPUT', '-o', interface, '-j', 'DROP',
                     '--concurrent'))

    return ebtables
def list_expired_virtual_machines():
    """
    Lists all expired VMs on the system.
    """
    now = time.time()
    expired_vms = []
    for vm in hedron.list_virtual_machines():
        vm_info = hedron.virtual_machine_info(vm)
        if vm_info['expiration'] != 0:
            # 0 means they never expire.
            if vm_info['expiration'] < now:
                time_delta = int(now - vm_info['expiration'])
                logging.debug('{} expired for {} seconds.'.format(vm,
                                                                  time_delta))
                expired_vms.append(vm)
            else:
                time_delta = int(vm_info['expiration'] - now)
                logging.debug('{} expiring in {} seconds.'.format(vm,
                                                                  time_delta))
    return expired_vms
Exemple #15
0
def slot_to_vm(slot):
    """
    Resolves a "slot40XX" to a VM ID.
    """
    if len(slot) != 8:
        raise ValueError('slot argument must be 8 characters.')

    slot = int(slot[4:])

    our_vm = None
    for vm in hedron.list_virtual_machines():
        vm_info = hedron.virtual_machine_info(vm)
        if vm_info['slot'] == slot:
            our_vm = vm
            break

    if our_vm is None:
        raise ValueError('VM does not exist for that slot.')
    else:
        return our_vm
def get_used_resources():
    """
    Returns all potentially allocated VM resource counts.
    """
    cores = 0
    memory = 0
    disk = 0
    ipv4_addresses = 0
    for vm in hedron.list_virtual_machines():
        vm_info = hedron.virtual_machine_info(vm)
        cores = cores + vm_info['cores']
        memory = memory + vm_info['memory']
        disk = disk + vm_info['disk']
        if vm_info['ipv4'] == '/32':
            ipv4_addresses = ipv4_addresses + 1
    return {
        'cores': cores,
        'memory': memory,
        'disk': disk,
        'ipv4_addresses': ipv4_addresses
    }
def existing_txids(currency):
    """
    Returns a list of txids already used for payment.
    This helps prevent a form of double spends.
    """
    txids = []

    if currency == 'settlement':
        return txids

    for vm in hedron.list_virtual_machines():
        vm_info = hedron.virtual_machine_info(vm)

        if 'txid' not in vm_info:
            continue

        # Legacy support. 2019-08-05
        if isinstance(vm_info['txid'], str):
            txids.append(vm_info['txid'])
        elif isinstance(vm_info['txid'], list):
            for txid in vm_info['txid']:
                txids.append(txid)

    return txids
def topup(options):
    """
    Topup a VM.
    """
    validate_options(options)

    machine_id = options['machine_id']
    expiration = options['expiration']
    currency = options['currency']
    # Here we take a single txid as string.
    txid = options['txid']

    vm_data = hedron.virtual_machine_info(options['machine_id'])
    logging.info('Topping up: {}'.format(options['machine_id']))

    vm_data['expiration'] = expiration
    vm_data['currency'] = currency
    # Legacy support as of 2019-08-05
    # Not likely one to be removed in the next year.
    if isinstance(vm_data['txid'], str):
        first_txid = vm_data['txid']
        vm_data['txid'] = [first_txid, txid]
    elif isinstance(vm_data['txid'], list):
        vm_data['txid'].append(txid)
    else:
        vm_data['txid'] = [txid]

    directory = '/var/tmp/runqemu/{}'.format(machine_id)
    json_file = os.path.join(directory, 'settings.json')
    topup_json_file = json_file + '.topup'
    with open(topup_json_file, 'x') as json_file_fp:
        json.dump(vm_data, json_file_fp)

    # Atomic update.
    shutil.move(topup_json_file, json_file)
    return True
Exemple #19
0
def boot_order_argument(vm):
    vm_info = hedron.virtual_machine_info(vm)
    bootorder = vm_info['bootorder']
    # Hacky... we provide the "n" via the CD drive's iPXE iso
    bootorder = bootorder.replace('n', 'd')
    return ['-boot', 'order={}'.format(bootorder)]
Exemple #20
0
def virtual_machine_topup(machine_id,
                          days,
                          currency,
                          refund_address=None,
                          override_code=None,
                          settlement_token=None,
                          affiliate_token=None,
                          affiliate_amount=None):
    config = get_and_validate_config()
    # We should have a draining for topups separately.
    # if config['draining'] is True:
    #     raise ValueError('Host is draining, unavailable for topups')
    if config['topup_enabled'] is False:
        raise ValueError('Host does not allow topups')

    return_data = {'latest_api_version': 2,
                   'payment': {'address': None, 'amount': 0},
                   'refund_tx': None,
                   'created': False,
                   'paid': False,
                   'warning': None,
                   'expiration': 1,
                   'txid': None}
    validate.machine_id(machine_id)
    validate.refund_address(refund_address)
    validate.currency(currency)
    validate.affiliate_amount(affiliate_amount)
    # settlement_token is validated in settlers.

    logging.info('topup request for {}'.format(machine_id))

    vm_data = hedron.virtual_machine_info(machine_id)
    if not override(override_code):
        if currency not in config['currencies']:
            msg = 'currency must be one of: {}'.format(config['currencies'])
            raise ValueError(msg)
    else:
        return_data['paid'] = True

    validate.days(days)

    if return_data['paid'] is False:
        address = config['currencies'][currency]
        # There is a bug with this, it uses the whole amount of bandwidth
        # over whatever timespan as the per-day calculation. So a 28 day server
        # at 32GiB per day will try to "pop up" at 28*32GiB per day and not
        # 32GiB per day.
        # bandwidth = vm_data['bandwidth']
        # Hack for now.
        bandwidth = 0
        cents = cost_in_cents(days=days,
                              cores=vm_data['cores'],
                              memory=vm_data['memory'],
                              disk=vm_data['disk'],
                              ipv4=vm_data['ipv4'],
                              ipv6=vm_data['ipv6'],
                              bandwidth=bandwidth)
        token = settlement_token
        business_token = config['settlers_business_token']
        pay = payment(machine_id,
                      currency,
                      cents,
                      address,
                      existing_txids=existing_txids(currency),
                      settlers_endpoint=config['settlers_endpoint'],
                      settlers_customer_token=token,
                      settlers_business_token=business_token,
                      monero_rpc=config['monero_rpc'],
                      affiliate_amount=affiliate_amount,
                      affiliate_token=affiliate_token)
        return_data['txid'] = pay.txid
        return_data['payment']['amount'] = pay.amount
        return_data['payment']['uri'] = pay.uri
        return_data['payment']['usd'] = pay.usd
        return_data['payment']['address'] = pay.address

        if return_data['txid'] is not None:
            return_data['paid'] = True

    expiration = days_to_expiration(days=days,
                                    current_expiration=vm_data['expiration'])
    topup_data = {'machine_id': machine_id,
                  'expiration': expiration,
                  'currency': currency,
                  'txid': return_data['txid']}
    return_data['expiration'] = expiration
    if return_data['paid'] is True:
        topup_vm_and_wait(topup_data)
        return_data['toppedup'] = True

    # toppedup and paid should always be the same, True or False.
    return return_data