def stop(fco_api, *args, **kwargs):
    try:
        server_uuid = ctx.instance.runtime_properties[RPROP_UUID]
        get_resource(fco_api, server_uuid, RT.SERVER)
    except (ConflictingResourceError, NoResourceError, KeyError):
        ctx.logger.warn("Server does not exist")
        return
    if get_server_status(fco_api, server_uuid) != enums.ServerStatus.STOPPED:
        if not stop_server(fco_api, server_uuid):
            raise Exception("Could not stop server!")
        ctx.logger.info("Server changed to STOPPED")
def create(fco_api, *args, **kwargs):
    ctx.logger.info('starting server creation')

    # Ease of access
    _rp = ctx.instance.runtime_properties
    _np = ctx.node.properties

    # Check if existing server is to be used
    if _np[PROP_USE_EXISTING]:
        server = get_resource(fco_api, _np[PROP_RESOURCE_ID, RT.SERVER])
        if not server.nics:
            raise Exception('No NICs attached to server')
        _rp[RPROP_UUID] = server.resourceUUID
        _rp[RPROP_DISKS] = [d.resourceUUID for d in server.disks]
        _rp[RPROP_NIC] = server.nics[0].resourceUUID
        _rp[RPROP_NICS] = [n.resourceUUID for n in server.nics]
        _rp[RPROP_IP] = server.nics[0].ipAddresses[0].ipAddress
        _rp[RPROP_USER] = server.initialUser
        _rp[RPROP_PASS] = server.initialPassword
        return (_rp[RPROP_UUID], _rp[RPROP_IP], _rp[RPROP_USER],
                _rp[RPROP_PASS])

    # Get configuration
    image = get_resource(fco_api, _np[PROP_IMAGE], RT.IMAGE)
    if _np[PROP_IMAGE]:
        vdc = get_resource(fco_api, _np[PROP_VDC], RT.VDC)
    else:
        vdc = None
    network = get_resource(fco_api, _np[PROP_NET], RT.NETWORK)
    server_po = get_resource(fco_api, _np[PROP_SERVER_PO], RT.PRODUCTOFFER)
    manager_key = get_resource(fco_api, _np[PROP_MANAGER_KEY], RT.SSHKEY)
    cpu_count = _np[PROP_CPU_COUNT]
    ram_amount = _np[PROP_RAM_AMOUNT]
    public_keys = _np[PROP_PUBLIC_KEYS] or []
    private_keys = _np[PROP_PRIVATE_KEYS] or []

    # Verify existence of private keys
    missing_keys = set()
    bad_permission_keys = set()
    key_contents = {}
    for key in private_keys:
        try:
            key_contents[key] = ctx.get_resource(os.path.expanduser(key))
        except NonRecoverableError as e:
            if 'HttpException: 404' in str(e):
                missing_keys.add(key)
            elif 'HttpException: 403' in str(e):
                bad_permission_keys.add(key)
            else:
                raise
    if missing_keys or bad_permission_keys:
        raise Exception('Missing private keys: {}\nBad permission keys: {}'
                        .format(missing_keys, bad_permission_keys))

    # Generate missing configuration
    image_uuid = image.resourceUUID
    if vdc is not None:
        cluster_uuid = vdc.clusterUUID
        vdc_uuid = vdc.resourceUUID
    else:
        cluster_uuid = image.clusterUUID
        vdc_uuid = image.vdcUUID
    network_uuid = network.resourceUUID
    network_type = network.networkType
    server_po_uuid = server_po.resourceUUID
    manager_key_uuid = manager_key.resourceUUID
    # TODO: better way of determining suitable disk
    boot_disk_po_uuid = get_resource(fco_api,
                                     '{} GB Storage Disk'.format(image.size),
                                     RT.PRODUCTOFFER).resourceUUID

    ctx.logger.info('Configuration: \n'
                    'image_uuid: %s\n'
                    'cluster_uuid: %s\n'
                    'vdc_uuid: %s\n'
                    'network_uuid: %s\n'
                    'server_po_uuid: %s\n'
                    'manager_key_uuid: %s\n'
                    'boot_disk_po_uuid: %s',
                    image_uuid, cluster_uuid, vdc_uuid, network_uuid,
                    server_po_uuid, manager_key_uuid, boot_disk_po_uuid)

    # Create server
    server_name = '{}{}_{}'.format(ctx.bootstrap_context.resources_prefix,
                                   ctx.deployment.id, ctx.instance.id)
    try:
        server_uuid = _rp[RPROP_UUID]
    except KeyError:
        # key_obj = get_resource(fco_api, key_uuid, RT.SSHKEY)
        # keys = SSHKey.REQUIRED_ATTRIBS.copy()
        # keys.add('resourceUUID')
        # submit_key = {}
        # for k in keys:
        #     try:
        #         submit_key[k] = getattr(manager_key, k)
        #     except AttributeError:
        #         submit_key[k] = None
        server_uuid = create_server(fco_api, server_po_uuid, image_uuid,
                                    cluster_uuid, vdc_uuid, cpu_count,
                                    ram_amount, boot_disk_po_uuid,
                                    [manager_key], server_name)
        _rp[RPROP_UUID] = server_uuid

    ctx.logger.info('server_uuid: %s', server_uuid)

    server = get_resource(fco_api, server_uuid, RT.SERVER)
    server_nics = [nic.resourceUUID for nic in server.nics]
    server_keys = [key.resourceUUID for key in server.sshkeys]

    # Wait for server to be active
    if not wait_for_state(fco_api, server_uuid, enums.ResourceState.ACTIVE,
                          RT.SERVER):
        raise Exception('Server failed to prepare in time!')

    ctx.logger.info('Server ACTIVE')

    # Add keys
    new_keys = set()
    for key in public_keys:
        if key not in server_keys:
            key_uuid = create_ssh_key(fco_api, key, server_name + ' Key')
            attach_ssh_key(fco_api, server_uuid, key_uuid)
            new_keys.add(key_uuid)

    ctx.logger.info('Keys attached: %s', new_keys)

    # Create NIC
    try:
        nic_uuid = _rp[RPROP_NIC]
    except KeyError:
        nic_uuid = create_nic(fco_api, cluster_uuid, network_type,
                              network_uuid, vdc_uuid, server_name + ' NIC')
        if not wait_for_state(fco_api, nic_uuid, enums.ResourceState.ACTIVE,
                              RT.NIC):
            raise Exception('NIC failed to create in time!')
        _rp[RPROP_NIC] = nic_uuid

    ctx.logger.info('nic_uuid: %s', nic_uuid)

    # Stop server if started
    if get_server_status(fco_api, server_uuid) != enums.ServerStatus.STOPPED:
        if not stop_server(fco_api, server_uuid):
            raise Exception('Stopping server failed to complete in time!')

    ctx.logger.info('Server STOPPED')

    # Attach NIC
    if nic_uuid not in server_nics:
        job_uuid = attach_nic(fco_api, server_uuid, nic_uuid, 1).resourceUUID
        cond = cobjects.Job.status == enums.JobStatus.SUCCESSFUL
        if not wait_for_cond(fco_api, job_uuid, cond, RT.JOB):
            raise Exception('Attaching NIC failed to complete in time!')
        ctx.logger.info('NICs attached')
    else:
        ctx.logger.info('NICs already attached')

    # Start server if not started
    if get_server_status(fco_api, server_uuid) == enums.ServerStatus.STOPPED:
        if not start_server(fco_api, server_uuid):
            raise Exception('Running server failed to complete in time!')

    ctx.logger.info('Server RUNNING')

    nic = get_resource(fco_api, nic_uuid, RT.NIC)
    server_ip = nic.ipAddresses[0].ipAddress
    server_port = 22

    ctx.logger.info('Server READY')

    username = server.initialUser
    password = server.initialPassword

    ssh_attempts = -1
    ssh_delay = 3

    # Fabric test

    while ssh_attempts:
        ctx.logger.info('Attempting to SSH ({})'.format(ssh_attempts))
        try:
            with settings(host_string=server_po_uuid, user=username,
                          password=password, disable_known_hosts=True,
                          abort_exception=Exception):
                run('mkdir ~/.ssh')
                run('chmod 0700 ~/.ssh')
                for key, key_content in key_contents.items():
                    remote = os.path.join('~', '.ssh', os.path.basename(key))
                    run('echo \'{}\' > {}'.format(key_content, remote))
                    run('chmod 0600 ' + remote)
                ctx.logger.info('Done')
                break
        except Exception as e:
            ctx.logger.info(e)
        ssh_attempts -= 1
    else:
        raise Exception('Failed to provision keys in time')

    # # Spur test
    # while ssh_attempts:
    #     ctx.logger.info('Attempting to SSH ({})'.format(ssh_attempts))
    #     shell = spur.SshShell(
    #         hostname=server_ip,
    #         port=server_port,
    #         username=username,
    #         password=password,
    #         shell_type=spur.ssh.ShellTypes.minimal,
    #         missing_host_key=spur.ssh.MissingHostKey.accept
    #     )
    #     with shell:
    #         try:
    #             ctx.logger.info('Creating & chmoding .ssh')
    #             shell.run(['mkdir', '~/.ssh'])
    #             shell.run(['chmod', '0700', '~/.ssh'])
    #             for key, key_content in key_contents.items():
    #                 ctx.logger.info('Adding private key: ' + remote)
    #                 remote = os.path.join('~', '.ssh', os.path.basename(key))
    #                 shell.run(['echo', "'{}'".format(key_content), '>',
    #                            remote])
    #                 shell.run(['chmod', '0600', remote])
    #         except spur.ssh.ConnectionError as e:
    #             if e.original_error[0] not in {errno.ECONNREFUSED,
    #                                         errno.EHOSTUNREACH}:
    #                 raise
    #             sleep(ssh_delay)
    #         ssh_attempts -= 1
    # else:
    #     raise Exception('Failed to provision keys in time')

    # # Provision private keys
    # ssh = SSHClient()
    # call(['ssh-keygen', '-R', server_ip])
    # ssh.set_missing_host_key_policy(AutoAddPolicy())
    #
    # while ssh_attempts:
    #     try:
    #         ctx.logger.info('Attempting to SSH ({})'.format(ssh_attempts))
    #         ctx.logger.info('SSH Connection details: {}'.format(
    #             ((server_ip, server_port, username, password, ssh_delay))))
    #         ssh.connect(server_ip, server_port, username, password,
    #                     timeout=ssh_delay, look_for_keys=False)
    #         ctx.logger.info('SSH connection established')
    #         break
    #     except socket.timeout:
    #         ssh_attempts -= 1
    #     except socket.error as e:
    #         if e[0] not in {errno.ECONNREFUSED, errno.EHOSTUNREACH}:
    #             ctx.logger.info('SSH connection failed: %s', e[0])
    #             raise
    #         sleep(ssh_delay)
    #     ssh_attempts -= 1
    # else:
    #     raise Exception('Failed to provision keys in time')
    # ssh.exec_command('mkdir ~/.ssh')
    # ssh.exec_command('chmod 0700 ~/.ssh')
    # for key, key_content in key_contents.items():
    #     remote = os.path.join('~', '.ssh', os.path.basename(key))
    #     ssh.exec_command('echo \'{}\' > {}'.format(key_content, remote))
    #     ssh.exec_command('chmod 0600 ' + remote)

    _rp[RPROP_UUID] = server_uuid
    _rp[RPROP_IP] = server_ip
    _rp[RPROP_USER] = username
    _rp[RPROP_PASS] = password

    server = get_resource(fco_api, server_uuid, RT.SERVER)
    _rp[RPROP_DISKS] = [d.resourceUUID for d in server.disks]
    _rp[RPROP_NICS] = [n.resourceUUID for n in server.nics]

    ctx.logger.info('Server IP: ' + server_ip)
    ctx.logger.info('Server User: '******'Server Password: ' + password)

    return server_uuid, server_ip, username, password
def stop(fco_api, *args, **kwargs):
    server_uuid = ctx.instance.runtime_properties.get(RPROP_UUID)
    if get_server_status(fco_api, server_uuid) != enums.ServerStatus.STOPPED:
        if not stop_server(fco_api, server_uuid):
            raise Exception('Could not stop server!')