Exemple #1
0
def console(vm, console_type):
    """Arrange for an OOB console of the specified type

    This method arranges for an OOB console of the specified type.
    Only consoles of type "vnc" are supported for now.

    It uses a running instance of vncauthproxy to setup proper
    VNC forwarding with a random password, then returns the necessary
    VNC connection info to the caller.

    """
    log.info("Get console  VM %s, type %s", vm, console_type)

    # Use RAPI to get VNC console information for this instance
    if vm.operstate != "STARTED":
        raise faults.BadRequest('Server not in ACTIVE state.')

    if settings.TEST:
        console_data = {'kind': 'vnc', 'host': 'ganeti_node', 'port': 1000}
    else:
        console_data = backend.get_instance_console(vm)

    if console_data['kind'] != 'vnc':
        message = 'got console of kind %s, not "vnc"' % console_data['kind']
        raise faults.ServiceUnavailable(message)

    # Let vncauthproxy decide on the source port.
    # The alternative: static allocation, e.g.
    # sport = console_data['port'] - 1000
    sport = 0
    daddr = console_data['host']
    dport = console_data['port']
    password = util.random_password()

    if settings.TEST:
        fwd = {'source_port': 1234, 'status': 'OK'}
    else:
        vnc_extra_opts = settings.CYCLADES_VNCAUTHPROXY_OPTS
        fwd = request_vnc_forwarding(sport, daddr, dport, password,
                                     **vnc_extra_opts)

    if fwd['status'] != "OK":
        raise faults.ServiceUnavailable('vncauthproxy returned error status')

    # Verify that the VNC server settings haven't changed
    if not settings.TEST:
        if console_data != backend.get_instance_console(vm):
            raise faults.ServiceUnavailable('VNC Server settings changed.')

    console = {
        'type': 'vnc',
        'host': getfqdn(),
        'port': fwd['source_port'],
        'password': password
    }

    return console
Exemple #2
0
 def get_client(self):
     """Get or create a client. """
     if self.offline:
         raise faults.ServiceUnavailable("Backend '%s' is offline" %
                                         self)
     return get_rapi_client(self.id, self.hash,
                            self.clustername,
                            self.port,
                            self.username,
                            self.password)
Exemple #3
0
def allocate_new_server(userid, flavor):
    """Allocate a new server to a Ganeti backend.

    Allocation is performed based on the owner of the server and the specified
    flavor. Also, backends that do not have a public IPv4 address are excluded
    from server allocation.

    This function runs inside a transaction, because after allocating the
    instance a commit must be performed in order to release all locks.

    """
    backend_allocator = BackendAllocator()
    use_backend = backend_allocator.allocate(userid, flavor)
    if use_backend is None:
        log.error("No available backend for VM with flavor %s", flavor)
        raise faults.ServiceUnavailable("No available backends")
    return use_backend
Exemple #4
0
def pooled_rapi_client(obj):
    if isinstance(obj, (VirtualMachine, BackendNetwork)):
        backend = obj.backend
    else:
        backend = obj

    if backend.offline:
        log.warning("Trying to connect with offline backend: %s", backend)
        raise faults.ServiceUnavailable("Cannot connect to offline"
                                        " backend: %s" % backend)

    b = backend
    client = get_rapi_client(b.id, b.hash, b.clustername, b.port, b.username,
                             b.password)
    try:
        yield client
    finally:
        put_rapi_client(client)
Exemple #5
0
def start_action(vm, action):
    log.debug("Applying action %s to VM %s", action, vm)
    if not action:
        return

    if not action in [x[0] for x in VirtualMachine.ACTIONS]:
        raise faults.ServiceUnavailable("Action %s not supported" % action)

    # No actions to deleted VMs
    if vm.deleted:
        raise faults.BadRequest("VirtualMachine has been deleted.")

    # No actions to machines being built. They may be destroyed, however.
    if vm.operstate == 'BUILD' and action != 'DESTROY':
        raise faults.BuildInProgress("Server is being build.")

    vm.action = action
    vm.backendjobid = None
    vm.backendopcode = None
    vm.backendjobstatus = None
    vm.backendlogmsg = None

    vm.save()
Exemple #6
0
def create(userid,
           name,
           flavor,
           link=None,
           mac_prefix=None,
           mode=None,
           floating_ip_pool=False,
           tags=None,
           public=False,
           drained=False,
           project=None,
           shared_to_project=False):
    if flavor is None:
        raise faults.BadRequest("Missing request parameter 'type'")
    elif flavor not in Network.FLAVORS.keys():
        raise faults.BadRequest("Invalid network type '%s'" % flavor)

    if mac_prefix is not None and flavor == "MAC_FILTERED":
        raise faults.BadRequest("Cannot override MAC_FILTERED mac-prefix")
    if link is not None and flavor == "PHYSICAL_VLAN":
        raise faults.BadRequest("Cannot override PHYSICAL_VLAN link")

    utils.check_name_length(name, Network.NETWORK_NAME_LENGTH, "Network name "
                            "is too long")

    try:
        fmode, flink, fmac_prefix, ftags = util.values_from_flavor(flavor)
    except EmptyPool:
        log.error("Failed to allocate resources for network of type: %s",
                  flavor)
        msg = "Failed to allocate resources for network."
        raise faults.ServiceUnavailable(msg)

    mode = mode or fmode
    link = link or flink
    mac_prefix = mac_prefix or fmac_prefix
    tags = tags or ftags

    validate_mac(mac_prefix + "0:00:00:00")

    # Check that given link is unique!
    if (link is not None and flavor == "IP_LESS_ROUTED"
            and Network.objects.filter(deleted=False, mode=mode,
                                       link=link).exists()):
        msg = "Link '%s' is already used." % link
        raise faults.BadRequest(msg)

    if project is None:
        project = userid

    network = Network.objects.create(name=name,
                                     userid=userid,
                                     project=project,
                                     shared_to_project=shared_to_project,
                                     flavor=flavor,
                                     mode=mode,
                                     link=link,
                                     mac_prefix=mac_prefix,
                                     tags=tags,
                                     public=public,
                                     external_router=public,
                                     floating_ip_pool=floating_ip_pool,
                                     action='CREATE',
                                     state='ACTIVE',
                                     drained=drained)

    if link is None:
        network.link = "%slink-%d" % (settings.BACKEND_PREFIX_ID, network.id)
        network.save()

    # Issue commission to Quotaholder and accept it since at the end of
    # this transaction the Network object will be created in the DB.
    # Note: the following call does a commit!
    if not public:
        quotas.issue_and_accept_commission(network)

    return network
Exemple #7
0
def create_network(request):
    # Normal Response Code: 202
    # Error Response Codes: computeFault (400, 500),
    #                       serviceUnavailable (503),
    #                       unauthorized (401),
    #                       badMediaType(415),
    #                       badRequest (400),
    #                       forbidden (403)
    #                       overLimit (413)

    try:
        req = utils.get_request_dict(request)
        log.info('create_network %s', req)

        user_id = request.user_uniq
        try:
            d = req['network']
            name = d['name']
        except KeyError:
            raise faults.BadRequest("Malformed request")

        # Get and validate flavor. Flavors are still exposed as 'type' in the
        # API.
        flavor = d.get("type", None)
        if flavor is None:
            raise faults.BadRequest("Missing request parameter 'type'")
        elif flavor not in Network.FLAVORS.keys():
            raise faults.BadRequest("Invalid network type '%s'" % flavor)
        elif flavor not in settings.API_ENABLED_NETWORK_FLAVORS:
            raise faults.Forbidden("Can not create network of type '%s'" %
                                   flavor)

        public = d.get("public", False)
        if public:
            raise faults.Forbidden("Can not create a public network.")

        dhcp = d.get('dhcp', True)

        # Get and validate network parameters
        subnet = d.get('cidr', '192.168.1.0/24')
        subnet6 = d.get('cidr6', None)
        gateway = d.get('gateway', None)
        gateway6 = d.get('gateway6', None)
        # Check that user provided a valid subnet
        util.validate_network_params(subnet, gateway, subnet6, gateway6)

        try:
            mode, link, mac_prefix, tags = util.values_from_flavor(flavor)
            validate_mac(mac_prefix + "0:00:00:00")
            network = Network.objects.create(
                name=name,
                userid=user_id,
                subnet=subnet,
                subnet6=subnet6,
                gateway=gateway,
                gateway6=gateway6,
                dhcp=dhcp,
                flavor=flavor,
                mode=mode,
                link=link,
                mac_prefix=mac_prefix,
                tags=tags,
                action='CREATE',
                state='ACTIVE')
        except EmptyPool:
            log.error("Failed to allocate resources for network of type: %s",
                      flavor)
            raise faults.ServiceUnavailable("Failed to allocate network"
                                            " resources")

        # Issue commission to Quotaholder and accept it since at the end of
        # this transaction the Network object will be created in the DB.
        # Note: the following call does a commit!
        quotas.issue_and_accept_commission(network)
    except:
        transaction.rollback()
        raise
    else:
        transaction.commit()

    networkdict = network_to_dict(network, request.user_uniq)
    response = render_network(request, networkdict, status=202)

    return response
Exemple #8
0
def console(vm, console_type):
    """Arrange for an OOB console of the specified type

    This method arranges for an OOB console of the specified type.
    Only consoles of type "vnc" are supported for now.

    It uses a running instance of vncauthproxy to setup proper
    VNC forwarding with a random password, then returns the necessary
    VNC connection info to the caller.

    """
    log.info("Get console  VM %s, type %s", vm, console_type)

    if vm.operstate != "STARTED":
        raise faults.BadRequest('Server not in ACTIVE state.')

    # Use RAPI to get VNC console information for this instance
    # RAPI GetInstanceConsole() returns endpoints to the vnc_bind_address,
    # which is a cluster-wide setting, either 0.0.0.0 or 127.0.0.1, and pretty
    # useless (see #783).
    #
    # Until this is fixed on the Ganeti side, construct a console info reply
    # directly.
    #
    # WARNING: This assumes that VNC runs on port network_port on
    #          the instance's primary node, and is probably
    #          hypervisor-specific.
    def get_console_data(i):
        return {"kind": "vnc", "host": i["pnode"], "port": i["network_port"]}

    with pooled_rapi_client(vm) as c:
        i = c.GetInstance(vm.backend_vm_id)
    console_data = get_console_data(i)

    if vm.backend.hypervisor == "kvm" and i['hvparams']['serial_console']:
        raise Exception("hv parameter serial_console cannot be true")

    # Check that the instance is really running
    if not i["oper_state"]:
        log.warning("VM '%s' is marked as '%s' in DB while DOWN in Ganeti",
                    vm.id, vm.operstate)
        # Instance is not running. Mock a shutdown job to sync DB
        backend.process_op_status(vm,
                                  etime=datetime.now(),
                                  jobid=0,
                                  opcode="OP_INSTANCE_SHUTDOWN",
                                  status="success",
                                  logmsg="Reconciliation simulated event")
        raise faults.BadRequest('Server not in ACTIVE state.')

    # Let vncauthproxy decide on the source port.
    # The alternative: static allocation, e.g.
    # sport = console_data['port'] - 1000
    sport = 0
    daddr = console_data['host']
    dport = console_data['port']
    password = util.random_password()

    vnc_extra_opts = settings.CYCLADES_VNCAUTHPROXY_OPTS

    # Maintain backwards compatibility with the dict setting
    if isinstance(vnc_extra_opts, list):
        vnc_extra_opts = choice(vnc_extra_opts)

    fwd = request_vnc_forwarding(sport,
                                 daddr,
                                 dport,
                                 password,
                                 console_type=console_type,
                                 **vnc_extra_opts)

    if fwd['status'] != "OK":
        log.error("vncauthproxy returned error status: '%s'" % fwd)
        raise faults.ServiceUnavailable('vncauthproxy returned error status')

    # Verify that the VNC server settings haven't changed
    with pooled_rapi_client(vm) as c:
        i = c.GetInstance(vm.backend_vm_id)
    if get_console_data(i) != console_data:
        raise faults.ServiceUnavailable('VNC Server settings changed.')

    try:
        host = fwd['proxy_address']
    except KeyError:
        host = getfqdn()

    console = {
        'type': console_type,
        'host': host,
        'port': fwd['source_port'],
        'password': password
    }

    return console
Exemple #9
0
def get_console(request, vm, args):
    """Arrange for an OOB console of the specified type

    This method arranges for an OOB console of the specified type.
    Only consoles of type "vnc" are supported for now.

    It uses a running instance of vncauthproxy to setup proper
    VNC forwarding with a random password, then returns the necessary
    VNC connection info to the caller.

    """
    # Normal Response Code: 200
    # Error Response Codes: computeFault (400, 500),
    #                       serviceUnavailable (503),
    #                       unauthorized (401),
    #                       badRequest (400),
    #                       badMediaType(415),
    #                       itemNotFound (404),
    #                       buildInProgress (409),
    #                       overLimit (413)

    log.info("Get console  VM %s", vm)
    console_type = args.get('type', '')
    if (console_type != 'vnc' and console_type != 'wsvnc'):
        raise faults.BadRequest('Type can only be "vnc" or "wsvnc."')

    # Use RAPI to get VNC console information for this instance
    if get_rsapi_state(vm) != 'ACTIVE':
        raise faults.BadRequest('Server not in ACTIVE state.')

    if settings.TEST:
        console_data = {'kind': 'vnc', 'host': 'ganeti_node', 'port': 1000}
    else:
        console_data = backend.get_instance_console(vm)

    if console_data['kind'] != 'vnc':
        message = 'got console of kind %s, not "vnc"' % console_data['kind']
        raise faults.ServiceUnavailable(message)

    # Let vncauthproxy decide on the source port.
    # The alternative: static allocation, e.g.
    # sport = console_data['port'] - 1000
    sport = 27631
    daddr = console_data['host']
    dport = console_data['port']
    password = random_password()

    if settings.TEST:
        fwd = {'source_port': 1234, 'status': 'OK'}
    else:
        fwd = request_vnc_forwarding(sport,
                                     daddr,
                                     dport,
                                     password,
                                     auth_user="******",
                                     auth_password="******",
                                     console_type=console_type)
        #fwd = request_vnc_forwarding(sport, daddr, dport, password)
    if fwd['status'] != "OK":
        raise faults.ServiceUnavailable('vncauthproxy returned error status')

    # Verify that the VNC server settings haven't changed
    if not settings.TEST:
        if console_data != backend.get_instance_console(vm):
            raise faults.ServiceUnavailable('VNC Server settings changed.')

    console = {
        'type': console_type,
        'host': getfqdn(),
        'port': fwd['source_port'],
        'password': password
    }

    if request.serialization == 'xml':
        mimetype = 'application/xml'
        data = render_to_string('console.xml', {'console': console})
    else:
        mimetype = 'application/json'
        data = json.dumps({'console': console})

    return HttpResponse(data, mimetype=mimetype, status=200)
Exemple #10
0
 def get_client(self):
     if self.backend:
         return self.backend.get_client()
     else:
         raise faults.ServiceUnavailable("VirtualMachine without backend")
Exemple #11
0
def do_create_server(userid,
                     name,
                     password,
                     flavor,
                     image,
                     metadata={},
                     personality=[],
                     network=None,
                     backend=None):
    # Fix flavor for archipelago
    disk_template, provider = util.get_flavor_provider(flavor)
    if provider:
        flavor.disk_template = disk_template
        flavor.disk_provider = provider
        flavor.disk_origin = image['checksum']
        image['backend_id'] = 'null'
    else:
        flavor.disk_provider = None
        flavor.disk_origin = None

    try:
        if backend is None:
            # Allocate backend to host the server.
            backend_allocator = BackendAllocator()
            backend = backend_allocator.allocate(userid, flavor)
            if backend is None:
                log.error("No available backend for VM with flavor %s", flavor)
                raise faults.ServiceUnavailable("No available backends")

        if network is None:
            # Allocate IP from public network
            (network, address) = util.get_public_ip(backend)
            nic = {'ip': address, 'network': network.backend_id}
        else:
            address = util.get_network_free_address(network)

        # We must save the VM instance now, so that it gets a valid
        # vm.backend_vm_id.
        vm = VirtualMachine.objects.create(name=name,
                                           backend=backend,
                                           userid=userid,
                                           imageid=image["id"],
                                           flavor=flavor,
                                           action="CREATE")

        # Create VM's public NIC. Do not wait notification form ganeti hooks to
        # create this NIC, because if the hooks never run (e.g. building error)
        # the VM's public IP address will never be released!
        NetworkInterface.objects.create(machine=vm,
                                        network=network,
                                        index=0,
                                        ipv4=address,
                                        state="BUILDING")

        log.info("Created entry in DB for VM '%s'", vm)

        # dispatch server created signal
        server_created.send(sender=vm,
                            created_vm_params={
                                'img_id': image['backend_id'],
                                'img_passwd': password,
                                'img_format': str(image['format']),
                                'img_personality': json.dumps(personality),
                                'img_properties':
                                json.dumps(image['metadata']),
                            })

        # Also we must create the VM metadata in the same transaction.
        for key, val in metadata.items():
            VirtualMachineMetadata.objects.create(meta_key=key,
                                                  meta_value=val,
                                                  vm=vm)
        # Issue commission to Quotaholder and accept it since at the end of
        # this transaction the VirtualMachine object will be created in the DB.
        # Note: the following call does a commit!
        quotas.issue_and_accept_commission(vm)
    except:
        transaction.rollback()
        raise
    else:
        transaction.commit()

    try:
        jobID = create_instance(vm, nic, flavor, image)
        # At this point the job is enqueued in the Ganeti backend
        vm.backendjobid = jobID
        vm.save()
        transaction.commit()
        log.info("User %s created VM %s, NIC %s, Backend %s, JobID %s", userid,
                 vm, nic, backend, str(jobID))
    except GanetiApiError as e:
        log.exception("Can not communicate to backend %s: %s.", backend, e)
        # Failed while enqueuing OP_INSTANCE_CREATE to backend. Restore
        # already reserved quotas by issuing a negative commission
        vm.operstate = "ERROR"
        vm.backendlogmsg = "Can not communicate to backend."
        vm.deleted = True
        vm.save()
        quotas.issue_and_accept_commission(vm, delete=True)
        raise
    except:
        transaction.rollback()
        raise

    return vm