Example #1
0
def destroy(name, call=None):
    '''
    Destroy a node. Will check termination protection and warn if enabled.

    CLI Example:

    .. code-block:: bash

        salt-cloud --destroy mymachine
    '''
    if call == 'function':
        raise SaltCloudSystemExit(
            'The destroy action must be called with -d, --destroy, '
            '-a or --action.')

    salt.utils.cloud.fire_event('event',
                                'destroying instance',
                                'salt/cloud/{0}/destroying'.format(name),
                                args={'name': name},
                                sock_dir=__opts__['sock_dir'],
                                transport=__opts__['transport'])

    data = show_instance(name, call='action')
    node = query(method='droplets',
                 droplet_id=data['id'],
                 http_method='delete')

    ## This is all terribly optomistic:
    # vm_ = get_vm_config(name=name)
    # delete_dns_record = config.get_cloud_config_value(
    #     'delete_dns_record', vm_, __opts__, search_global=False, default=None,
    # )
    # TODO: when _vm config data can be made available, we should honor the configuration settings,
    # but until then, we should assume stale DNS records are bad, and default behavior should be to
    # delete them if we can. When this is resolved, also resolve the comments a couple of lines below.
    delete_dns_record = True

    if not isinstance(delete_dns_record, bool):
        raise SaltCloudConfigError(
            '\'delete_dns_record\' should be a boolean value.')
    # When the "to do" a few lines up is resolved, remove these lines and use the if/else logic below.
    log.debug('Deleting DNS records for {0}.'.format(name))
    destroy_dns_records(name)

    # Until the "to do" from line 754 is taken care of, we don't need this logic.
    # if delete_dns_record:
    #    log.debug('Deleting DNS records for {0}.'.format(name))
    #    destroy_dns_records(name)
    # else:
    #    log.debug('delete_dns_record : {0}'.format(delete_dns_record))
    #    for line in pprint.pformat(dir()).splitlines():
    #       log.debug('delete  context: {0}'.format(line))

    salt.utils.cloud.fire_event('event',
                                'destroyed instance',
                                'salt/cloud/{0}/destroyed'.format(name),
                                args={'name': name},
                                sock_dir=__opts__['sock_dir'],
                                transport=__opts__['transport'])

    if __opts__.get('update_cachedir', False) is True:
        salt.utils.cloud.delete_minion_cachedir(
            name,
            __active_provider_name__.split(':')[0], __opts__)

    return node
Example #2
0
def create(vm_):
    """
    Create a single VM from a data dict
    """
    try:
        # Check for required profile parameters before sending any API calls.
        if (vm_["profile"] and config.is_profile_configured(
                __opts__,
                __active_provider_name__ or "softlayer_hw",
                vm_["profile"],
                vm_=vm_,
        ) is False):
            return False
    except AttributeError:
        pass

    name = vm_["name"]
    hostname = name
    domain = config.get_cloud_config_value("domain",
                                           vm_,
                                           __opts__,
                                           default=None)
    if domain is None:
        SaltCloudSystemExit(
            "A domain name is required for the SoftLayer driver.")

    if vm_.get("use_fqdn"):
        name = ".".join([name, domain])
        vm_["name"] = name

    __utils__["cloud.fire_event"](
        "event",
        "starting create",
        "salt/cloud/{0}/creating".format(name),
        args=__utils__["cloud.filter_event"](
            "creating", vm_, ["name", "profile", "provider", "driver"]),
        sock_dir=__opts__["sock_dir"],
        transport=__opts__["transport"],
    )

    log.info("Creating Cloud VM %s", name)
    conn = get_conn(service="SoftLayer_Product_Order")
    kwargs = {
        "complexType":
        "SoftLayer_Container_Product_Order_Hardware_Server",
        "quantity":
        1,
        "hardware": [{
            "hostname": hostname,
            "domain": domain
        }],
        # Baremetal Package
        "packageId":
        50,
        "prices": [
            # Size Ex: 1921: 2 x 2.0 GHz Core Bare Metal Instance - 2 GB Ram
            {
                "id": vm_["size"]
            },
            # HDD Ex: 19: 250GB SATA II
            {
                "id": vm_["hdd"]
            },
            # Image Ex: 13963: CentOS 6.0 - Minimal Install (64 bit)
            {
                "id": vm_["image"]
            },
            # The following items are currently required
            # Reboot / Remote Console
            {
                "id": "905"
            },
            # 1 IP Address
            {
                "id": "21"
            },
            # Host Ping Monitoring
            {
                "id": "55"
            },
            # Email and Ticket Notifications
            {
                "id": "57"
            },
            # Automated Notification Response
            {
                "id": "58"
            },
            # Unlimited SSL VPN Users & 1 PPTP VPN User per account
            {
                "id": "420"
            },
            # Nessus Vulnerability Assessment & Reporting
            {
                "id": "418"
            },
        ],
    }

    optional_products = config.get_cloud_config_value("optional_products",
                                                      vm_,
                                                      __opts__,
                                                      default=[])
    for product in optional_products:
        kwargs["prices"].append({"id": product})

    # Default is 273 (100 Mbps Public & Private Networks)
    port_speed = config.get_cloud_config_value("port_speed",
                                               vm_,
                                               __opts__,
                                               default=273)
    kwargs["prices"].append({"id": port_speed})

    # Default is 1800 (0 GB Bandwidth)
    bandwidth = config.get_cloud_config_value("bandwidth",
                                              vm_,
                                              __opts__,
                                              default=1800)
    kwargs["prices"].append({"id": bandwidth})

    post_uri = config.get_cloud_config_value("post_uri",
                                             vm_,
                                             __opts__,
                                             default=None)
    if post_uri:
        kwargs["prices"].append({"id": post_uri})

    vlan_id = config.get_cloud_config_value("vlan",
                                            vm_,
                                            __opts__,
                                            default=False)
    if vlan_id:
        kwargs["primaryNetworkComponent"] = {"networkVlan": {"id": vlan_id}}

    location = get_location(vm_)
    if location:
        kwargs["location"] = location

    __utils__["cloud.fire_event"](
        "event",
        "requesting instance",
        "salt/cloud/{0}/requesting".format(name),
        args={
            "kwargs":
            __utils__["cloud.filter_event"]("requesting", kwargs,
                                            list(kwargs)),
        },
        sock_dir=__opts__["sock_dir"],
        transport=__opts__["transport"],
    )

    try:
        response = conn.placeOrder(kwargs)
        # Leaving the following line in, commented, for easy debugging
        # response = conn.verifyOrder(kwargs)
    except Exception as exc:  # pylint: disable=broad-except
        log.error(
            "Error creating %s on SoftLayer\n\n"
            "The following exception was thrown when trying to "
            "run the initial deployment: \n%s",
            name,
            exc,
            # Show the traceback if the debug logging level is enabled
            exc_info_on_loglevel=logging.DEBUG,
        )
        return False

    def wait_for_ip():
        """
        Wait for the IP address to become available
        """
        nodes = list_nodes_full()
        if "primaryIpAddress" in nodes[hostname]:
            return nodes[hostname]["primaryIpAddress"]
        time.sleep(1)
        return False

    ip_address = salt.utils.cloud.wait_for_fun(
        wait_for_ip,
        timeout=config.get_cloud_config_value("wait_for_fun_timeout",
                                              vm_,
                                              __opts__,
                                              default=15 * 60),
    )

    ssh_connect_timeout = config.get_cloud_config_value(
        # 15 minutes
        "ssh_connect_timeout",
        vm_,
        __opts__,
        900,
    )
    if not salt.utils.cloud.wait_for_port(ip_address,
                                          timeout=ssh_connect_timeout):
        raise SaltCloudSystemExit("Failed to authenticate against remote ssh")

    pass_conn = get_conn(service="SoftLayer_Account")
    mask = {
        "virtualGuests": {
            "powerState": "",
            "operatingSystem": {
                "passwords": ""
            }
        },
    }

    def get_passwd():
        """
        Wait for the password to become available
        """
        node_info = pass_conn.getVirtualGuests(id=response["id"], mask=mask)
        for node in node_info:
            if (node["id"] == response["id"]
                    and "passwords" in node["operatingSystem"]
                    and node["operatingSystem"]["passwords"]):
                return node["operatingSystem"]["passwords"][0]["password"]
        time.sleep(5)
        return False

    passwd = salt.utils.cloud.wait_for_fun(
        get_passwd,
        timeout=config.get_cloud_config_value("wait_for_fun_timeout",
                                              vm_,
                                              __opts__,
                                              default=15 * 60),
    )
    response["password"] = passwd
    response["public_ip"] = ip_address

    ssh_username = config.get_cloud_config_value("ssh_username",
                                                 vm_,
                                                 __opts__,
                                                 default="root")

    vm_["ssh_host"] = ip_address
    vm_["password"] = passwd
    ret = __utils__["cloud.bootstrap"](vm_, __opts__)

    ret.update(response)

    __utils__["cloud.fire_event"](
        "event",
        "created instance",
        "salt/cloud/{0}/created".format(name),
        args=__utils__["cloud.filter_event"](
            "created", vm_, ["name", "profile", "provider", "driver"]),
        sock_dir=__opts__["sock_dir"],
        transport=__opts__["transport"],
    )

    return ret
Example #3
0
def _salt(fun, *args, **kw):
    '''Execute a salt function on a specific minion

    Special kwargs:

            salt_target
                target to exec things on
            salt_timeout
                timeout for jobs
            salt_job_poll
                poll interval to wait for job finish result
    '''
    try:
        poll = kw.pop('salt_job_poll')
    except KeyError:
        poll = 0.1
    try:
        target = kw.pop('salt_target')
    except KeyError:
        target = None
    try:
        timeout = int(kw.pop('salt_timeout'))
    except (KeyError, ValueError):
        # try to has some low timeouts for very basic commands
        timeout = __FUN_TIMEOUT.get(
            fun,
            900  # wait up to 15 minutes for the default timeout
        )
    try:
        kwargs = kw.pop('kwargs')
    except KeyError:
        kwargs = {}
    if not target:
        infos = get_configured_provider()
        if not infos:
            return
        target = infos['target']
    laps = time.time()
    cache = False
    if fun in __CACHED_FUNS:
        cache = True
        laps = laps // __CACHED_FUNS[fun]
    try:
        sargs = json.dumps(args)
    except TypeError:
        sargs = ''
    try:
        skw = json.dumps(kw)
    except TypeError:
        skw = ''
    try:
        skwargs = json.dumps(kwargs)
    except TypeError:
        skwargs = ''
    cache_key = (laps, target, fun, sargs, skw, skwargs)
    if not cache or (cache and (cache_key not in __CACHED_CALLS)):
        conn = _client()
        runner = _runner()
        rkwargs = kwargs.copy()
        rkwargs['timeout'] = timeout
        rkwargs.setdefault('expr_form', 'list')
        kwargs.setdefault('expr_form', 'list')
        ping_retries = 0
        # the target(s) have environ one minute to respond
        # we call 60 ping request, this prevent us
        # from blindly send commands to unmatched minions
        ping_max_retries = 60
        ping = True
        # do not check ping... if we are pinguing
        if fun == 'test.ping':
            ping_retries = ping_max_retries + 1
        # be sure that the executors are alive
        while ping_retries <= ping_max_retries:
            try:
                if ping_retries > 0:
                    time.sleep(1)
                pings = conn.cmd(tgt=target, timeout=10, fun='test.ping')
                values = list(pings.values())
                if not values:
                    ping = False
                for v in values:
                    if v is not True:
                        ping = False
                if not ping:
                    raise ValueError('Unreachable')
                break
            except Exception:
                ping = False
                ping_retries += 1
                log.error('{0} unreachable, retrying'.format(target))
        if not ping:
            raise SaltCloudSystemExit('Target {0} unreachable'.format(target))
        jid = conn.cmd_async(tgt=target,
                             fun=fun,
                             arg=args,
                             kwarg=kw,
                             **rkwargs)
        cret = conn.cmd(tgt=target,
                        fun='saltutil.find_job',
                        arg=[jid],
                        timeout=10,
                        **kwargs)
        running = bool(cret.get(target, False))
        endto = time.time() + timeout
        while running:
            rkwargs = {
                'tgt': target,
                'fun': 'saltutil.find_job',
                'arg': [jid],
                'timeout': 10
            }
            cret = conn.cmd(**rkwargs)
            running = bool(cret.get(target, False))
            if not running:
                break
            if running and (time.time() > endto):
                raise Exception('Timeout {0}s for {1} is elapsed'.format(
                    timeout, pformat(rkwargs)))
            time.sleep(poll)
        # timeout for the master to return data about a specific job
        wait_for_res = float({
            'test.ping': '5',
        }.get(fun, '120'))
        while wait_for_res:
            wait_for_res -= 0.5
            cret = runner.cmd('jobs.lookup_jid', [jid, {'__kwarg__': True}])
            if target in cret:
                ret = cret[target]
                break
            # recent changes
            elif 'data' in cret and 'outputter' in cret:
                ret = cret['data']
                break
            # special case, some answers may be crafted
            # to handle the unresponsivness of a specific command
            # which is also meaningful, e.g. a minion not yet provisioned
            if fun in ['test.ping'] and not wait_for_res:
                ret = {
                    'test.ping': False,
                }.get(fun, False)
            time.sleep(0.5)
        try:
            if 'is not available.' in ret:
                raise SaltCloudSystemExit(
                    'module/function {0} is not available'.format(fun))
        except SaltCloudSystemExit:
            raise
        except TypeError:
            pass
        if cache:
            __CACHED_CALLS[cache_key] = ret
    elif cache and cache_key in __CACHED_CALLS:
        ret = __CACHED_CALLS[cache_key]
    return ret
Example #4
0
def create(server_):
    '''
    Create a single BareMetal server from a data dict.
    '''
    try:
        # Check for required profile parameters before sending any API calls.
        if server_['profile'] and config.is_profile_configured(
                __opts__,
                __active_provider_name__ or 'scaleway',
                server_['profile'],
                vm_=server_) is False:
            return False
    except AttributeError:
        pass

    __utils__['cloud.fire_event'](
        'event',
        'starting create',
        'salt/cloud/{0}/creating'.format(server_['name']),
        args=__utils__['cloud.filter_event'](
            'creating', server_, ['name', 'profile', 'provider', 'driver']),
        sock_dir=__opts__['sock_dir'],
        transport=__opts__['transport'])

    log.info('Creating a BareMetal server %s', server_['name'])

    access_key = config.get_cloud_config_value('access_key',
                                               get_configured_provider(),
                                               __opts__,
                                               search_global=False)

    commercial_type = config.get_cloud_config_value('commercial_type',
                                                    server_,
                                                    __opts__,
                                                    default='C1')

    key_filename = config.get_cloud_config_value('ssh_key_file',
                                                 server_,
                                                 __opts__,
                                                 search_global=False,
                                                 default=None)

    if key_filename is not None and not os.path.isfile(key_filename):
        raise SaltCloudConfigError(
            'The defined key_filename \'{0}\' does not exist'.format(
                key_filename))

    ssh_password = config.get_cloud_config_value('ssh_password', server_,
                                                 __opts__)

    kwargs = {
        'name': server_['name'],
        'organization': access_key,
        'image': get_image(server_),
        'commercial_type': commercial_type,
    }

    __utils__['cloud.fire_event'](
        'event',
        'requesting instance',
        'salt/cloud/{0}/requesting'.format(server_['name']),
        args={
            'kwargs':
            __utils__['cloud.filter_event']('requesting', kwargs,
                                            list(kwargs)),
        },
        sock_dir=__opts__['sock_dir'],
        transport=__opts__['transport'])

    try:
        ret = create_node(kwargs)
    except Exception as exc:
        log.error(
            'Error creating %s on Scaleway\n\n'
            'The following exception was thrown when trying to '
            'run the initial deployment: %s',
            server_['name'],
            exc,
            # Show the traceback if the debug logging level is enabled
            exc_info_on_loglevel=logging.DEBUG)
        return False

    def __query_node_data(server_name):
        ''' Called to check if the server has a public IP address.
        '''
        data = show_instance(server_name, 'action')
        if data and data.get('public_ip'):
            return data
        return False

    try:
        data = salt.utils.cloud.wait_for_ip(
            __query_node_data,
            update_args=(server_['name'], ),
            timeout=config.get_cloud_config_value('wait_for_ip_timeout',
                                                  server_,
                                                  __opts__,
                                                  default=10 * 60),
            interval=config.get_cloud_config_value('wait_for_ip_interval',
                                                   server_,
                                                   __opts__,
                                                   default=10),
        )
    except (SaltCloudExecutionTimeout, SaltCloudExecutionFailure) as exc:
        try:
            # It might be already up, let's destroy it!
            destroy(server_['name'])
        except SaltCloudSystemExit:
            pass
        finally:
            raise SaltCloudSystemExit(six.text_type(exc))

    server_['ssh_host'] = data['public_ip']['address']
    server_['ssh_password'] = ssh_password
    server_['key_filename'] = key_filename
    ret = __utils__['cloud.bootstrap'](server_, __opts__)

    ret.update(data)

    log.info('Created BareMetal server \'%s\'', server_['name'])
    log.debug('\'%s\' BareMetal server creation details:\n%s', server_['name'],
              pprint.pformat(data))

    __utils__['cloud.fire_event'](
        'event',
        'created instance',
        'salt/cloud/{0}/created'.format(server_['name']),
        args=__utils__['cloud.filter_event'](
            'created', server_, ['name', 'profile', 'provider', 'driver']),
        sock_dir=__opts__['sock_dir'],
        transport=__opts__['transport'])

    return ret
Example #5
0
def create(server_):
    """
    Create a single BareMetal server from a data dict.
    """
    try:
        # Check for required profile parameters before sending any API calls.
        if (server_["profile"] and config.is_profile_configured(
                __opts__,
                __active_provider_name__ or "scaleway",
                server_["profile"],
                vm_=server_,
        ) is False):
            return False
    except AttributeError:
        pass

    __utils__["cloud.fire_event"](
        "event",
        "starting create",
        "salt/cloud/{}/creating".format(server_["name"]),
        args=__utils__["cloud.filter_event"](
            "creating", server_, ["name", "profile", "provider", "driver"]),
        sock_dir=__opts__["sock_dir"],
        transport=__opts__["transport"],
    )

    log.info("Creating a BareMetal server %s", server_["name"])

    access_key = config.get_cloud_config_value("access_key",
                                               get_configured_provider(),
                                               __opts__,
                                               search_global=False)

    commercial_type = config.get_cloud_config_value("commercial_type",
                                                    server_,
                                                    __opts__,
                                                    default="C1")

    key_filename = config.get_cloud_config_value("ssh_key_file",
                                                 server_,
                                                 __opts__,
                                                 search_global=False,
                                                 default=None)

    if key_filename is not None and not os.path.isfile(key_filename):
        raise SaltCloudConfigError(
            "The defined key_filename '{}' does not exist".format(
                key_filename))

    ssh_password = config.get_cloud_config_value("ssh_password", server_,
                                                 __opts__)

    kwargs = {
        "name": server_["name"],
        "organization": access_key,
        "image": get_image(server_),
        "commercial_type": commercial_type,
    }

    __utils__["cloud.fire_event"](
        "event",
        "requesting instance",
        "salt/cloud/{}/requesting".format(server_["name"]),
        args={
            "kwargs":
            __utils__["cloud.filter_event"]("requesting", kwargs,
                                            list(kwargs)),
        },
        sock_dir=__opts__["sock_dir"],
        transport=__opts__["transport"],
    )

    try:
        ret = create_node(kwargs)
    except Exception as exc:  # pylint: disable=broad-except
        log.error(
            "Error creating %s on Scaleway\n\n"
            "The following exception was thrown when trying to "
            "run the initial deployment: %s",
            server_["name"],
            exc,
            # Show the traceback if the debug logging level is enabled
            exc_info_on_loglevel=logging.DEBUG,
        )
        return False

    def __query_node_data(server_name):
        """ Called to check if the server has a public IP address.
        """
        data = show_instance(server_name, "action")
        if data and data.get("public_ip"):
            return data
        return False

    try:
        data = salt.utils.cloud.wait_for_ip(
            __query_node_data,
            update_args=(server_["name"], ),
            timeout=config.get_cloud_config_value("wait_for_ip_timeout",
                                                  server_,
                                                  __opts__,
                                                  default=10 * 60),
            interval=config.get_cloud_config_value("wait_for_ip_interval",
                                                   server_,
                                                   __opts__,
                                                   default=10),
        )
    except (SaltCloudExecutionTimeout, SaltCloudExecutionFailure) as exc:
        try:
            # It might be already up, let's destroy it!
            destroy(server_["name"])
        except SaltCloudSystemExit:
            pass
        finally:
            raise SaltCloudSystemExit(str(exc))

    server_["ssh_host"] = data["public_ip"]["address"]
    server_["ssh_password"] = ssh_password
    server_["key_filename"] = key_filename
    ret = __utils__["cloud.bootstrap"](server_, __opts__)

    ret.update(data)

    log.info("Created BareMetal server '%s'", server_["name"])
    log.debug(
        "'%s' BareMetal server creation details:\n%s",
        server_["name"],
        pprint.pformat(data),
    )

    __utils__["cloud.fire_event"](
        "event",
        "created instance",
        "salt/cloud/{}/created".format(server_["name"]),
        args=__utils__["cloud.filter_event"](
            "created", server_, ["name", "profile", "provider", "driver"]),
        sock_dir=__opts__["sock_dir"],
        transport=__opts__["transport"],
    )

    return ret
Example #6
0
def create_lb(kwargs=None, call=None):
    r'''
    Create a load-balancer configuration.
    CLI Example:

    .. code-block:: bash

        salt-cloud -f create_lb dimensiondata \
            name=dev-lb port=80 protocol=http \
            members=w1,w2,w3 algorithm=ROUND_ROBIN
    '''
    conn = get_conn()
    if call != 'function':
        raise SaltCloudSystemExit(
            'The create_lb function must be called with -f or --function.'
        )

    if not kwargs or 'name' not in kwargs:
        log.error(
            'A name must be specified when creating a health check.'
        )
        return False
    if 'port' not in kwargs:
        log.error(
            'A port or port-range must be specified for the load-balancer.'
        )
        return False
    if 'networkdomain' not in kwargs:
        log.error(
            'A network domain must be specified for the load-balancer.'
        )
        return False
    if 'members' in kwargs:
        members = []
        ip = ""
        membersList = kwargs.get('members').split(',')
        log.debug('MemberList: %s', membersList)
        for member in membersList:
            try:
                log.debug('Member: %s', member)
                node = get_node(conn, member)
                log.debug('Node: %s', node)
                ip = node.private_ips[0]
            except Exception as err:
                log.error(
                    'Failed to get node ip: %s', err,
                    # Show the traceback if the debug logging level is enabled
                    exc_info_on_loglevel=logging.DEBUG
                )
            members.append(Member(ip, ip, kwargs['port']))
    else:
        members = None
    log.debug('Members: %s', members)

    networkdomain = kwargs['networkdomain']
    name = kwargs['name']
    port = kwargs['port']
    protocol = kwargs.get('protocol', None)
    algorithm = kwargs.get('algorithm', None)

    lb_conn = get_lb_conn(conn)
    network_domains = conn.ex_list_network_domains()
    network_domain = [y for y in network_domains if y.name == networkdomain][0]

    log.debug('Network Domain: %s', network_domain.id)
    lb_conn.ex_set_current_network_domain(network_domain.id)

    __utils__['cloud.fire_event'](
        'event',
        'create load_balancer',
        'salt/cloud/loadbalancer/creating',
        args=kwargs,
        sock_dir=__opts__['sock_dir'],
        transport=__opts__['transport']
    )

    lb = lb_conn.create_balancer(
        name, port, protocol, algorithm, members
    )

    __utils__['cloud.fire_event'](
        'event',
        'created load_balancer',
        'salt/cloud/loadbalancer/created',
        args=kwargs,
        sock_dir=__opts__['sock_dir'],
        transport=__opts__['transport']
    )
    return _expand_balancer(lb)
Example #7
0
def create(vm_):
    '''
    Create a single VM from a data dict

    CLI Example:

    .. code-block:: bash

        salt-cloud -p profile_name vm_name
    '''
    deploy = config.get_cloud_config_value('deploy', vm_, __opts__)
    key_filename = config.get_cloud_config_value('private_key',
                                                 vm_,
                                                 __opts__,
                                                 search_global=False,
                                                 default=None)
    if deploy is True and key_filename is None and \
            salt.utils.which('sshpass') is None:
        raise SaltCloudSystemExit(
            'Cannot deploy salt in a VM if the \'private_key\' setting '
            'is not set and \'sshpass\' binary is not present on the '
            'system for the password.')

    salt.utils.cloud.fire_event('event',
                                'starting create',
                                'salt/cloud/{0}/creating'.format(vm_['name']),
                                {
                                    'name': vm_['name'],
                                    'profile': vm_['profile'],
                                    'provider': vm_['provider'],
                                },
                                transport=__opts__['transport'])

    log.info('Creating Cloud VM {0} in {1}'.format(
        vm_['name'], vm_.get('location', DEFAULT_LOCATION)))

    ## added . for fqdn hostnames
    salt.utils.cloud.check_name(vm_['name'], 'a-zA-Z0-9-.')
    kwargs = {
        'name': vm_['name'],
        'image': get_image(vm_),
        'size': get_size(vm_),
        'location': vm_.get('location', DEFAULT_LOCATION)
    }

    salt.utils.cloud.fire_event('event',
                                'requesting instance',
                                'salt/cloud/{0}/requesting'.format(
                                    vm_['name']), {'kwargs': kwargs},
                                transport=__opts__['transport'])

    try:
        data = create_node(**kwargs)
    except Exception as exc:
        log.error(
            'Error creating {0} on JOYENT\n\n'
            'The following exception was thrown when trying to '
            'run the initial deployment: \n{1}'.format(vm_['name'], str(exc)),
            # Show the traceback if the debug logging level is enabled
            exc_info_on_loglevel=logging.DEBUG)
        return False

    ret = {}

    def __query_node_data(vm_id, vm_location):
        rcode, data = query(command='my/machines/{0}'.format(vm_id),
                            method='GET',
                            location=vm_location)
        if rcode not in VALID_RESPONSE_CODES:
            # Trigger a wait for IP error
            return False

        if data['state'] != 'running':
            # Still not running, trigger another iteration
            return

        if isinstance(data['ips'], list) and len(data['ips']) > 0:
            return data

    if 'ips' in data:
        if isinstance(data['ips'], list) and len(data['ips']) <= 0:
            log.info('New joyent asynchronous machine creation api detected...'
                     '\n\t\t-- please wait for IP addresses to be assigned...')
        try:
            data = salt.utils.cloud.wait_for_ip(
                __query_node_data,
                update_args=(data['id'], vm_.get('location',
                                                 DEFAULT_LOCATION)),
                timeout=config.get_cloud_config_value('wait_for_ip_timeout',
                                                      vm_,
                                                      __opts__,
                                                      default=5 * 60),
                interval=config.get_cloud_config_value('wait_for_ip_interval',
                                                       vm_,
                                                       __opts__,
                                                       default=1),
            )
        except (SaltCloudExecutionTimeout, SaltCloudExecutionFailure) as exc:
            try:
                # It might be already up, let's destroy it!
                destroy(vm_['name'])
            except SaltCloudSystemExit:
                pass
            finally:
                raise SaltCloudSystemExit(str(exc))

    data = reformat_node(data)

    ssh_username = config.get_cloud_config_value('ssh_username',
                                                 vm_,
                                                 __opts__,
                                                 default='root')

    if config.get_cloud_config_value('deploy', vm_, __opts__) is True:
        host = data['public_ips'][0]
        salt_host = data['public_ips'][0]
        if ssh_interface(vm_) == 'private_ips':
            host = data['private_ips'][0]
        if get_salt_interface(vm_) == 'private_ips':
            salt_host = data['private_ips'][0]

        deploy_script = script(vm_)
        deploy_kwargs = {
            'opts':
            __opts__,
            'host':
            host,
            'salt_host':
            salt_host,
            'username':
            ssh_username,
            'key_filename':
            key_filename,
            'script':
            deploy_script.script,
            'name':
            vm_['name'],
            'tmp_dir':
            config.get_cloud_config_value('tmp_dir',
                                          vm_,
                                          __opts__,
                                          default='/tmp/.saltcloud'),
            'deploy_command':
            config.get_cloud_config_value(
                'deploy_command',
                vm_,
                __opts__,
                default='/tmp/.saltcloud/deploy.sh',
            ),
            'start_action':
            __opts__['start_action'],
            'parallel':
            __opts__['parallel'],
            'sock_dir':
            __opts__['sock_dir'],
            'conf_file':
            __opts__['conf_file'],
            'minion_pem':
            vm_['priv_key'],
            'minion_pub':
            vm_['pub_key'],
            'keep_tmp':
            __opts__['keep_tmp'],
            'preseed_minion_keys':
            vm_.get('preseed_minion_keys', None),
            'sudo':
            config.get_cloud_config_value('sudo',
                                          vm_,
                                          __opts__,
                                          default=(ssh_username != 'root')),
            'sudo_password':
            config.get_cloud_config_value('sudo_password',
                                          vm_,
                                          __opts__,
                                          default=None),
            'tty':
            config.get_cloud_config_value('tty', vm_, __opts__, default=True),
            'display_ssh_output':
            config.get_cloud_config_value('display_ssh_output',
                                          vm_,
                                          __opts__,
                                          default=True),
            'script_args':
            config.get_cloud_config_value('script_args', vm_, __opts__),
            'script_env':
            config.get_cloud_config_value('script_env', vm_, __opts__),
            'minion_conf':
            salt.utils.cloud.minion_config(__opts__, vm_)
        }

        # Deploy salt-master files, if necessary
        if config.get_cloud_config_value('make_master', vm_, __opts__) is True:
            deploy_kwargs['make_master'] = True
            deploy_kwargs['master_pub'] = vm_['master_pub']
            deploy_kwargs['master_pem'] = vm_['master_pem']
            master_conf = salt.utils.cloud.master_config(__opts__, vm_)
            deploy_kwargs['master_conf'] = master_conf

            if master_conf.get('syndic_master', None):
                deploy_kwargs['make_syndic'] = True

        deploy_kwargs['make_minion'] = config.get_cloud_config_value(
            'make_minion', vm_, __opts__, default=True)

        # Check for Windows install params
        win_installer = config.get_cloud_config_value('win_installer', vm_,
                                                      __opts__)
        if win_installer:
            deploy_kwargs['win_installer'] = win_installer
            minion = salt.utils.cloud.minion_config(__opts__, vm_)
            deploy_kwargs['master'] = minion['master']
            deploy_kwargs['username'] = config.get_cloud_config_value(
                'win_username', vm_, __opts__, default='Administrator')
            deploy_kwargs['password'] = config.get_cloud_config_value(
                'win_password', vm_, __opts__, default='')

        # Store what was used to the deploy the VM
        event_kwargs = copy.deepcopy(deploy_kwargs)
        del event_kwargs['minion_pem']
        del event_kwargs['minion_pub']
        del event_kwargs['sudo_password']
        if 'password' in event_kwargs:
            del event_kwargs['password']
        ret['deploy_kwargs'] = event_kwargs

        salt.utils.cloud.fire_event('event',
                                    'executing deploy script',
                                    'salt/cloud/{0}/deploying'.format(
                                        vm_['name']), {'kwargs': event_kwargs},
                                    transport=__opts__['transport'])

        deployed = False
        if win_installer:
            deployed = salt.utils.cloud.deploy_windows(**deploy_kwargs)
        else:
            deployed = salt.utils.cloud.deploy_script(**deploy_kwargs)

        if deployed:
            log.info('Salt installed on {0}'.format(vm_['name']))
        else:
            log.error('Failed to start Salt on Cloud VM {0}'.format(
                vm_['name']))

    ret.update(data)

    log.info('Created Cloud VM {0[name]!r}'.format(vm_))
    log.debug('{0[name]!r} VM creation details:\n{1}'.format(
        vm_, pprint.pformat(data)))

    salt.utils.cloud.fire_event('event',
                                'created instance',
                                'salt/cloud/{0}/created'.format(vm_['name']), {
                                    'name': vm_['name'],
                                    'profile': vm_['profile'],
                                    'provider': vm_['provider'],
                                },
                                transport=__opts__['transport'])

    return ret
Example #8
0
def destroy(name, call=None):
    ''' Destroy a node.

    .. versionadded:: 2018.3.0

    Disconnect a minion from the master, and remove its keys.

    Optionally, (if ``remove_config_on_destroy`` is ``True``),
      disables salt-minion from running on the minion, and
      erases the Salt configuration files from it.

    Optionally, (if ``shutdown_on_destroy`` is ``True``),
      orders the minion to halt.

    CLI Example:

    .. code-block:: bash

        salt-cloud --destroy mymachine

    '''
    if call == 'function':
        raise SaltCloudSystemExit(
            'The destroy action must be called with -d, --destroy, '
            '-a, or --action.')

    opts = __opts__

    __utils__['cloud.fire_event']('event',
                                  'destroying instance',
                                  'salt/cloud/{0}/destroying'.format(name),
                                  args={
                                      'name': name
                                  },
                                  sock_dir=opts['sock_dir'],
                                  transport=opts['transport'])

    vm_ = get_configured_provider()
    local = salt.client.LocalClient()
    my_info = local.cmd(name, 'grains.get', ['salt-cloud'])
    try:
        vm_.update(my_info[name])  # get profile name to get config value
    except (IndexError, TypeError):
        pass
    if config.get_cloud_config_value('remove_config_on_destroy',
                                     vm_,
                                     opts,
                                     default=True):
        ret = local.cmd(
            name,  # prevent generating new keys on restart
            'service.disable',
            ['salt-minion'])
        if ret and ret[name]:
            log.info('disabled salt-minion service on %s', name)
        ret = local.cmd(name, 'config.get', ['conf_file'])
        if ret and ret[name]:
            confile = ret[name]
            ret = local.cmd(name, 'file.remove', [confile])
            if ret and ret[name]:
                log.info('removed minion %s configuration file %s', name,
                         confile)
        ret = local.cmd(name, 'config.get', ['pki_dir'])
        if ret and ret[name]:
            pki_dir = ret[name]
            ret = local.cmd(name, 'file.remove', [pki_dir])
            if ret and ret[name]:
                log.info('removed minion %s key files in %s', name, pki_dir)

    if config.get_cloud_config_value('shutdown_on_destroy',
                                     vm_,
                                     opts,
                                     default=False):
        ret = local.cmd(name, 'system.shutdown')
        if ret and ret[name]:
            log.info('system.shutdown for minion %s successful', name)

    __utils__['cloud.fire_event']('event',
                                  'destroyed instance',
                                  'salt/cloud/{0}/destroyed'.format(name),
                                  args={
                                      'name': name
                                  },
                                  sock_dir=opts['sock_dir'],
                                  transport=opts['transport'])

    return {'Destroyed': '{0} was destroyed.'.format(name)}
Example #9
0
def destroy(name, conn=None, call=None):
    '''
    Delete a single VM
    '''
    if call == 'function':
        raise SaltCloudSystemExit(
            'The destroy action must be called with -d, --destroy, '
            '-a or --action.')

    __utils__['cloud.fire_event']('event',
                                  'destroying instance',
                                  'salt/cloud/{0}/destroying'.format(name),
                                  args={
                                      'name': name
                                  },
                                  sock_dir=__opts__['sock_dir'],
                                  transport=__opts__['transport'])

    if not conn:
        conn = get_conn()  # pylint: disable=E0602

    node = conn.server_by_name(name)
    profiles = get_configured_provider()['profiles']  # pylint: disable=E0602
    if node is None:
        log.error('Unable to find the VM %s', name)
    profile = None
    if 'metadata' in node.extra and 'profile' in node.extra['metadata']:
        profile = node.extra['metadata']['profile']

    flush_mine_on_destroy = False
    if profile and profile in profiles and 'flush_mine_on_destroy' in profiles[
            profile]:
        flush_mine_on_destroy = profiles[profile]['flush_mine_on_destroy']

    if flush_mine_on_destroy:
        log.info('Clearing Salt Mine: %s', name)
        salt_client = salt.client.get_local_client(__opts__['conf_file'])
        minions = salt_client.cmd(name, 'mine.flush')

    log.info('Clearing Salt Mine: %s, %s', name, flush_mine_on_destroy)
    log.info('Destroying VM: %s', name)
    ret = conn.delete(node.id)
    if ret:
        log.info('Destroyed VM: %s', name)
        # Fire destroy action
        __utils__['cloud.fire_event']('event',
                                      'destroyed instance',
                                      'salt/cloud/{0}/destroyed'.format(name),
                                      args={
                                          'name': name
                                      },
                                      sock_dir=__opts__['sock_dir'],
                                      transport=__opts__['transport'])
        if __opts__.get('delete_sshkeys', False) is True:
            salt.utils.cloud.remove_sshkey(
                getattr(node, __opts__.get('ssh_interface', 'public_ips'))[0])
        if __opts__.get('update_cachedir', False) is True:
            __utils__['cloud.delete_minion_cachedir'](
                name, __active_provider_name__.split(':')[0], __opts__)
        __utils__['cloud.cachedir_index_del'](name)
        return True

    log.error('Failed to Destroy VM: %s', name)
    return False
Example #10
0
def create(vm_):
    '''
    Create a single VM from a data dict
    '''
    try:
        # Check for required profile parameters before sending any API calls.
        if vm_['profile'] and config.is_profile_configured(
                __opts__,
                __active_provider_name__ or 'nova',
                vm_['profile'],
                vm_=vm_) is False:
            return False
    except AttributeError:
        pass

    deploy = config.get_cloud_config_value('deploy', vm_, __opts__)
    key_filename = config.get_cloud_config_value('ssh_key_file',
                                                 vm_,
                                                 __opts__,
                                                 search_global=False,
                                                 default=None)
    if key_filename is not None and not os.path.isfile(key_filename):
        raise SaltCloudConfigError(
            'The defined ssh_key_file \'{0}\' does not exist'.format(
                key_filename))

    vm_['key_filename'] = key_filename

    # Since using "provider: <provider-engine>" is deprecated, alias provider
    # to use driver: "driver: <provider-engine>"
    if 'provider' in vm_:
        vm_['driver'] = vm_.pop('provider')

    salt.utils.cloud.fire_event('event',
                                'starting create',
                                'salt/cloud/{0}/creating'.format(vm_['name']),
                                {
                                    'name': vm_['name'],
                                    'profile': vm_['profile'],
                                    'provider': vm_['driver'],
                                },
                                transport=__opts__['transport'])
    conn = get_conn()

    if 'instance_id' in vm_:
        # This was probably created via another process, and doesn't have
        # things like salt keys created yet, so let's create them now.
        if 'pub_key' not in vm_ and 'priv_key' not in vm_:
            log.debug('Generating minion keys for \'{0[name]}\''.format(vm_))
            vm_['priv_key'], vm_['pub_key'] = salt.utils.cloud.gen_keys(
                salt.config.get_cloud_config_value('keysize', vm_, __opts__))
        data = conn.server_show_libcloud(vm_['instance_id'])
        if vm_['key_filename'] is None and 'change_password' in __opts__ and __opts__[
                'change_password'] is True:
            vm_['password'] = sup.secure_password()
            conn.root_password(vm_['instance_id'], vm_['password'])
    else:
        # Put together all of the information required to request the instance,
        # and then fire off the request for it
        data, vm_ = request_instance(vm_)

        # Pull the instance ID, valid for both spot and normal instances
        vm_['instance_id'] = data.id

    def __query_node_data(vm_, data):
        try:
            node = show_instance(vm_['name'], 'action')
            log.debug('Loaded node data for {0}:\n{1}'.format(
                vm_['name'], pprint.pformat(node)))
        except Exception as err:
            log.error(
                'Failed to get nodes list: {0}'.format(err),
                # Show the traceback if the debug logging level is enabled
                exc_info_on_loglevel=logging.DEBUG)
            # Trigger a failure in the wait for IP function
            return False

        running = node['state'] == 'ACTIVE'
        if not running:
            # Still not running, trigger another iteration
            return

        if rackconnect(vm_) is True:
            extra = node.get('extra', {})
            rc_status = extra.get('metadata',
                                  {}).get('rackconnect_automation_status', '')
            if rc_status != 'DEPLOYED':
                log.debug('Waiting for Rackconnect automation to complete')
                return

        if managedcloud(vm_) is True:
            extra = conn.server_show_libcloud(node['id']).extra
            mc_status = extra.get('metadata',
                                  {}).get('rax_service_level_automation', '')

            if mc_status != 'Complete':
                log.debug('Waiting for managed cloud automation to complete')
                return

        access_ip = node.get('extra', {}).get('access_ip', '')

        rcv3 = rackconnectv3(vm_) in node['addresses']
        sshif = ssh_interface(vm_) in node['addresses']

        if any((rcv3, sshif)):
            networkname = rackconnectv3(vm_) if rcv3 else ssh_interface(vm_)
            for network in node['addresses'].get(networkname, []):
                if network['version'] is 4:
                    access_ip = network['addr']
                    break
            vm_['cloudnetwork'] = True

        # Conditions to pass this
        #
        #     Rackconnect v2: vm_['rackconnect'] = True
        #         If this is True, then the server will not be accessible from the ipv4 addres in public_ips.
        #         That interface gets turned off, and an ipv4 from the dedicated firewall is routed to the
        #         server.  In this case we can use the private_ips for ssh_interface, or the access_ip.
        #
        #     Rackconnect v3: vm['rackconnectv3'] = <cloudnetwork>
        #         If this is the case, salt will need to use the cloud network to login to the server.  There
        #         is no ipv4 address automatically provisioned for these servers when they are booted.  SaltCloud
        #         also cannot use the private_ips, because that traffic is dropped at the hypervisor.
        #
        #     CloudNetwork: vm['cloudnetwork'] = True
        #         If this is True, then we should have an access_ip at this point set to the ip on the cloud
        #         network.  If that network does not exist in the 'addresses' dictionary, then SaltCloud will
        #         use the initial access_ip, and not overwrite anything.

        if any((cloudnetwork(vm_),
                rackconnect(vm_))) and (ssh_interface(vm_) != 'private_ips'
                                        or rcv3):
            data.public_ips = [
                access_ip,
            ]
            return data

        result = []

        if 'private_ips' not in node and 'public_ips' not in node and \
           'floating_ips' not in node and 'fixed_ips' not in node and \
           'access_ip' in node.get('extra', {}):
            result = [node['extra']['access_ip']]

        private = node.get('private_ips', [])
        public = node.get('public_ips', [])
        fixed = node.get('fixed_ips', [])
        floating = node.get('floating_ips', [])

        if private and not public:
            log.warning('Private IPs returned, but not public... Checking for '
                        'misidentified IPs')
            for private_ip in private:
                private_ip = preferred_ip(vm_, [private_ip])
                if salt.utils.cloud.is_public_ip(private_ip):
                    log.warning('{0} is a public IP'.format(private_ip))
                    data.public_ips.append(private_ip)
                    log.warning(
                        ('Public IP address was not ready when we last'
                         ' checked.  Appending public IP address now.'))
                    public = data.public_ips
                else:
                    log.warning('{0} is a private IP'.format(private_ip))
                    ignore_ip = ignore_cidr(vm_, private_ip)
                    if private_ip not in data.private_ips and not ignore_ip:
                        result.append(private_ip)

        # populate return data with private_ips
        # when ssh_interface is set to private_ips and public_ips exist
        if not result and ssh_interface(vm_) == 'private_ips':
            for private_ip in private:
                ignore_ip = ignore_cidr(vm_, private_ip)
                if private_ip not in data.private_ips and not ignore_ip:
                    result.append(private_ip)

        non_private_ips = []

        if public:
            data.public_ips = public
            if ssh_interface(vm_) == 'public_ips':
                non_private_ips.append(public)

        if floating:
            data.floating_ips = floating
            if ssh_interface(vm_) == 'floating_ips':
                non_private_ips.append(floating)

        if fixed:
            data.fixed_ips = fixed
            if ssh_interface(vm_) == 'fixed_ips':
                non_private_ips.append(fixed)

        if non_private_ips:
            log.debug('result = {0}'.format(non_private_ips))
            data.private_ips = result
            if ssh_interface(vm_) != 'private_ips':
                return data

        if result:
            log.debug('result = {0}'.format(result))
            data.private_ips = result
            if ssh_interface(vm_) == 'private_ips':
                return data

    try:
        data = salt.utils.cloud.wait_for_ip(
            __query_node_data,
            update_args=(vm_, data),
            timeout=config.get_cloud_config_value('wait_for_ip_timeout',
                                                  vm_,
                                                  __opts__,
                                                  default=10 * 60),
            interval=config.get_cloud_config_value('wait_for_ip_interval',
                                                   vm_,
                                                   __opts__,
                                                   default=10),
        )
    except (SaltCloudExecutionTimeout, SaltCloudExecutionFailure) as exc:
        try:
            # It might be already up, let's destroy it!
            destroy(vm_['name'])
        except SaltCloudSystemExit:
            pass
        finally:
            raise SaltCloudSystemExit(str(exc))

    log.debug('VM is now running')

    if ssh_interface(vm_) == 'private_ips':
        ip_address = preferred_ip(vm_, data.private_ips)
    elif ssh_interface(vm_) == 'fixed_ips':
        ip_address = preferred_ip(vm_, data.fixed_ips)
    elif ssh_interface(vm_) == 'floating_ips':
        ip_address = preferred_ip(vm_, data.floating_ips)
    else:
        ip_address = preferred_ip(vm_, data.public_ips)
    log.debug('Using IP address {0}'.format(ip_address))

    if salt.utils.cloud.get_salt_interface(vm_, __opts__) == 'private_ips':
        salt_ip_address = preferred_ip(vm_, data.private_ips)
        log.info('Salt interface set to: {0}'.format(salt_ip_address))
    elif salt.utils.cloud.get_salt_interface(vm_, __opts__) == 'fixed_ips':
        salt_ip_address = preferred_ip(vm_, data.fixed_ips)
        log.info('Salt interface set to: {0}'.format(salt_ip_address))
    elif salt.utils.cloud.get_salt_interface(vm_, __opts__) == 'floating_ips':
        salt_ip_address = preferred_ip(vm_, data.floating_ips)
        log.info('Salt interface set to: {0}'.format(salt_ip_address))
    else:
        salt_ip_address = preferred_ip(vm_, data.public_ips)
        log.debug('Salt interface set to: {0}'.format(salt_ip_address))

    if not ip_address:
        raise SaltCloudSystemExit('A valid IP address was not found')

    vm_['ssh_host'] = ip_address
    vm_['salt_host'] = salt_ip_address

    ret = salt.utils.cloud.bootstrap(vm_, __opts__)

    ret.update(data.__dict__)

    if 'password' in ret['extra']:
        del ret['extra']['password']

    log.info('Created Cloud VM \'{0[name]}\''.format(vm_))
    log.debug('\'{0[name]}\' VM creation details:\n{1}'.format(
        vm_, pprint.pformat(data.__dict__)))

    event_data = {
        'name': vm_['name'],
        'profile': vm_['profile'],
        'provider': vm_['driver'],
        'instance_id': vm_['instance_id'],
        'floating_ips': data.floating_ips,
        'fixed_ips': data.fixed_ips,
        'private_ips': data.private_ips,
        'public_ips': data.public_ips
    }

    salt.utils.cloud.fire_event('event',
                                'created instance',
                                'salt/cloud/{0}/created'.format(vm_['name']),
                                event_data,
                                transport=__opts__['transport'])
    salt.utils.cloud.cachedir_index_add(vm_['name'], vm_['profile'], 'nova',
                                        vm_['driver'])
    return ret
Example #11
0
File: linode.py Project: yi9/salt
def boot(name=None, kwargs=None, call=None):
    '''
    Boot a Linode.

    name
        The name of the Linode to boot. Can be used instead of ``linode_id``.

    linode_id
        The ID of the Linode to boot. If provided, will be used as an
        alternative to ``name`` and reduces the number of API calls to
        Linode by one. Will be preferred over ``name``.

    config_id
        The ID of the Config to boot. Required.

    check_running
        Defaults to True. If set to False, overrides the call to check if
        the VM is running before calling the linode.boot API call. Change
        ``check_running`` to True is useful during the boot call in the
        create function, since the new VM will not be running yet.

    Can be called as an action (which requires a name):

    .. code-block:: bash

        salt-cloud -a boot my-instance config_id=10

    ...or as a function (which requires either a name or linode_id):

    .. code-block:: bash

        salt-cloud -f boot my-linode-config name=my-instance config_id=10
        salt-cloud -f boot my-linode-config linode_id=1225876 config_id=10
    '''
    if name is None and call == 'action':
        raise SaltCloudSystemExit(
            'The boot action requires a \'name\'.'
        )

    if kwargs is None:
        kwargs = {}

    linode_id = kwargs.get('linode_id', None)
    config_id = kwargs.get('config_id', None)
    check_running = kwargs.get('check_running', True)

    if call == 'function':
        name = kwargs.get('name', None)

    if name is None and linode_id is None:
        raise SaltCloudSystemExit(
            'The boot function requires either a \'name\' or a \'linode_id\'.'
        )

    if config_id is None:
        raise SaltCloudSystemExit(
            'The boot function requires a \'config_id\'.'
        )

    if linode_id is None:
        linode_id = get_linode_id_from_name(name)
        linode_item = name
    else:
        linode_item = linode_id

    # Check if Linode is running first
    if check_running is True:
        status = get_linode(kwargs={'linode_id': linode_id})['STATUS']
        if status == '1':
            raise SaltCloudSystemExit(
                'Cannot boot Linode {0}. '
                'Linode {0} is already running.'.format(linode_item)
            )

    # Boot the VM and get the JobID from Linode
    response = _query('linode', 'boot',
                      args={'LinodeID': linode_id,
                            'ConfigID': config_id})['DATA']
    boot_job_id = response['JobID']

    if not _wait_for_job(linode_id, boot_job_id):
        log.error('Boot failed for Linode {0}.'.format(linode_item))
        return False

    return True
Example #12
0
def request_instance(vm_=None, call=None):
    '''
    Put together all of the information necessary to request an instance
    through Novaclient and then fire off the request the instance.

    Returns data about the instance
    '''
    if call == 'function':
        # Technically this function may be called other ways too, but it
        # definitely cannot be called with --function.
        raise SaltCloudSystemExit(
            'The request_instance action must be called with -a or --action.')
    log.info('Creating Cloud VM {0}'.format(vm_['name']))
    salt.utils.cloud.check_name(vm_['name'], 'a-zA-Z0-9._-')
    conn = get_conn()
    kwargs = vm_.copy()

    try:
        kwargs['image_id'] = get_image(conn, vm_)
    except Exception as exc:
        raise SaltCloudSystemExit('Error creating {0} on OPENSTACK\n\n'
                                  'Could not find image {1}: {2}\n'.format(
                                      vm_['name'], vm_['image'], exc))

    try:
        kwargs['flavor_id'] = get_size(conn, vm_)
    except Exception as exc:
        raise SaltCloudSystemExit('Error creating {0} on OPENSTACK\n\n'
                                  'Could not find size {1}: {2}\n'.format(
                                      vm_['name'], vm_['size'], exc))

    kwargs['key_name'] = config.get_cloud_config_value('ssh_key_name',
                                                       vm_,
                                                       __opts__,
                                                       search_global=False)

    security_groups = config.get_cloud_config_value('security_groups',
                                                    vm_,
                                                    __opts__,
                                                    search_global=False)
    if security_groups is not None:
        vm_groups = security_groups.split(',')
        avail_groups = conn.secgroup_list()
        group_list = []

        for vmg in vm_groups:
            if vmg in [name for name, details in six.iteritems(avail_groups)]:
                group_list.append(vmg)
            else:
                raise SaltCloudNotFound(
                    'No such security group: \'{0}\''.format(vmg))

        kwargs['security_groups'] = group_list

    avz = config.get_cloud_config_value('availability_zone',
                                        vm_,
                                        __opts__,
                                        default=None,
                                        search_global=False)
    if avz is not None:
        kwargs['availability_zone'] = avz

    kwargs['nics'] = config.get_cloud_config_value('networks',
                                                   vm_,
                                                   __opts__,
                                                   search_global=False,
                                                   default=None)

    files = config.get_cloud_config_value('files',
                                          vm_,
                                          __opts__,
                                          search_global=False)
    if files:
        kwargs['files'] = {}
        for src_path in files:
            if os.path.exists(files[src_path]):
                with salt.utils.fopen(files[src_path], 'r') as fp_:
                    kwargs['files'][src_path] = fp_.read()
            else:
                kwargs['files'][src_path] = files[src_path]

    userdata_file = config.get_cloud_config_value('userdata_file',
                                                  vm_,
                                                  __opts__,
                                                  search_global=False)

    if userdata_file is not None:
        with salt.utils.fopen(userdata_file, 'r') as fp:
            kwargs['userdata'] = fp.read()

    kwargs['config_drive'] = config.get_cloud_config_value('config_drive',
                                                           vm_,
                                                           __opts__,
                                                           search_global=False)

    kwargs.update(get_block_mapping_opts(vm_))

    salt.utils.cloud.fire_event(
        'event',
        'requesting instance',
        'salt/cloud/{0}/requesting'.format(vm_['name']), {
            'kwargs': {
                'name': kwargs['name'],
                'image': kwargs.get('image_id', 'Boot From Volume'),
                'size': kwargs['flavor_id']
            }
        },
        transport=__opts__['transport'])

    try:
        data = conn.boot(**kwargs)
    except Exception as exc:
        raise SaltCloudSystemExit(
            'Error creating {0} on Nova\n\n'
            'The following exception was thrown by libcloud when trying to '
            'run the initial deployment: {1}\n'.format(vm_['name'], exc))
    if data.extra.get('password', None) is None and vm_.get(
            'key_filename', None) is None:
        raise SaltCloudSystemExit('No password returned.  Set ssh_key_file.')

    floating_ip_conf = config.get_cloud_config_value('floating_ip',
                                                     vm_,
                                                     __opts__,
                                                     search_global=False)
    if floating_ip_conf.get('auto_assign', False):
        pool = floating_ip_conf.get('pool', 'public')
        for fl_ip, opts in conn.floating_ip_list().iteritems():
            if opts['instance_id'] is None and opts['pool'] == pool:
                floating_ip = fl_ip
                break
            else:
                floating_ip = conn.floating_ip_create(pool)

        try:
            conn.floating_ip_associate(kwargs['name'], floating_ip)
            vm_['floating_ip'] = floating_ip
        except Exception as exc:
            raise SaltCloudSystemExit(
                'Error assigning floating_ip for {0} on Nova\n\n'
                'The following exception was thrown by libcloud when trying to '
                'assing a floating ip: {1}\n'.format(vm_['name'], exc))

    vm_['password'] = data.extra.get('password', '')

    return data, vm_
Example #13
0
def destroy(name, call=None):
    """
    This function irreversibly destroys a virtual machine on the cloud provider.
    Before doing so, it should fire an event on the Salt event bus.

    The tag for this event is `salt/cloud/<vm name>/destroying`.
    Once the virtual machine has been destroyed, another event is fired.
    The tag for that event is `salt/cloud/<vm name>/destroyed`.

    Dependencies:
        list_nodes

    @param name:
    @type name: str
    @param call:
    @type call:
    @return: True if all went well, otherwise an error message
    @rtype: bool|str
    """
    log.info("Attempting to delete instance {0}".format(name))

    if call == 'function':
        raise SaltCloudSystemExit(
            'The destroy action must be called with -d, --destroy, '
            '-a or --action.'
        )

    found = []

    providers = __opts__.get('providers', {})
    providers_to_check = [_f for _f in [cfg.get('libvirt') for cfg in six.itervalues(providers)] if _f]
    for provider in providers_to_check:
        conn = __get_conn(provider['url'])
        log.info("looking at {0}".format(provider['url']))
        try:
            domain = conn.lookupByName(name)
            found.append({'domain': domain, 'conn': conn})
        except libvirtError:
            pass

    if not found:
        return "{0} doesn't exist and can't be deleted".format(name)

    if len(found) > 1:
        return "{0} doesn't identify a unique machine leaving things".format(name)

    __utils__['cloud.fire_event'](
        'event',
        'destroying instance',
        'salt/cloud/{0}/destroying'.format(name),
        {'name': name},
        sock_dir=__opts__['sock_dir'],
        transport=__opts__['transport']
    )

    destroy_domain(found[0]['conn'], found[0]['domain'])

    __utils__['cloud.fire_event'](
        'event',
        'destroyed instance',
        'salt/cloud/{0}/destroyed'.format(name),
        {'name': name},
        sock_dir=__opts__['sock_dir'],
        transport=__opts__['transport']
    )
Example #14
0
def create(vm_):
    '''
    Provision a single machine
    '''
    clone_strategy = vm_.get('clone_strategy') or 'full'

    if clone_strategy not in set(['quick', 'full']):
        raise SaltCloudSystemExit("'clone_strategy' must be one of quick or full. Got '{0}'".format(clone_strategy))

    ip_source = vm_.get('ip_source') or 'ip-learning'

    if ip_source not in set(['ip-learning', 'qemu-agent']):
        raise SaltCloudSystemExit("'ip_source' must be one of qemu-agent or ip-learning. Got '{0}'".format(ip_source))

    log.info("Cloning machine '{0}' with strategy '{1}'".format(vm_['name'], clone_strategy))

    try:
        # Check for required profile parameters before sending any API calls.
        if vm_['profile'] and config.is_profile_configured(__opts__,
                                                           __active_provider_name__ or 'libvirt',
                                                           vm_['profile']) is False:
            return False
    except AttributeError:
        pass

    # TODO: check name qemu/libvirt will choke on some characters (like '/')?
    name = vm_['name']

    __utils__['cloud.fire_event'](
        'event',
        'starting create',
        'salt/cloud/{0}/creating'.format(name),
        args=__utils__['cloud.filter_event']('creating', vm_, ['name', 'profile', 'provider', 'driver']),
        sock_dir=__opts__['sock_dir'],
        transport=__opts__['transport']
    )

    key_filename = config.get_cloud_config_value(
        'private_key', vm_, __opts__, search_global=False, default=None
    )
    if key_filename is not None and not os.path.isfile(key_filename):
        raise SaltCloudConfigError(
            'The defined key_filename \'{0}\' does not exist'.format(
                key_filename
            )
        )
    vm_['key_filename'] = key_filename
    # wait_for_instance requires private_key
    vm_['private_key'] = key_filename

    cleanup = []
    try:
        # clone the vm
        base = vm_['base_domain']
        conn = __get_conn(vm_['url'])

        try:
            # for idempotency the salt-bootstrap needs -F argument
            #  script_args: -F
            clone_domain = conn.lookupByName(name)
        except libvirtError as e:
            domain = conn.lookupByName(base)
            # TODO: ensure base is shut down before cloning
            xml = domain.XMLDesc(0)

            kwargs = {
                'name': name,
                'base_domain': base,
            }

            __utils__['cloud.fire_event'](
                'event',
                'requesting instance',
                'salt/cloud/{0}/requesting'.format(name),
                args={
                    'kwargs': __utils__['cloud.filter_event']('requesting', kwargs, kwargs.keys()),
                },
                sock_dir=__opts__['sock_dir'],
                transport=__opts__['transport']
            )

            log.debug("Source machine XML '{0}'".format(xml))

            domain_xml = ElementTree.fromstring(xml)
            domain_xml.find('./name').text = name
            if domain_xml.find('./description') is None:
                description_elem = ElementTree.Element('description')
                domain_xml.insert(0, description_elem)
            description = domain_xml.find('./description')
            description.text = "Cloned from {0}".format(base)
            domain_xml.remove(domain_xml.find('./uuid'))

            for iface_xml in domain_xml.findall('./devices/interface'):
                iface_xml.remove(iface_xml.find('./mac'))
                # enable IP learning, this might be a default behaviour...
                if iface_xml.find("./filterref/parameter[@name='CTRL_IP_LEARNING']") is None:
                    iface_xml.append(ElementTree.fromstring(IP_LEARNING_XML))

            # If a qemu agent is defined we need to fix the path to its socket
            # <channel type='unix'>
            #   <source mode='bind' path='/var/lib/libvirt/qemu/channel/target/domain-<dom-name>/org.qemu.guest_agent.0'/>
            #   <target type='virtio' name='org.qemu.guest_agent.0'/>
            #   <address type='virtio-serial' controller='0' bus='0' port='2'/>
            # </channel>
            for agent_xml in domain_xml.findall("""./devices/channel[@type='unix']"""):
                #  is org.qemu.guest_agent.0 an option?
                if agent_xml.find("""./target[@type='virtio'][@name='org.qemu.guest_agent.0']""") is not None:
                    source_element = agent_xml.find("""./source[@mode='bind']""")
                    # see if there is a path element that needs rewriting
                    if source_element and 'path' in source_element.attrib:
                        path = source_element.attrib['path']
                        new_path = path.replace('/domain-{0}/'.format(base), '/domain-{0}/'.format(name))
                        log.debug("Rewriting agent socket path to {0}".format(new_path))
                        source_element.attrib['path'] = new_path

            for disk in domain_xml.findall("""./devices/disk[@device='disk'][@type='file']"""):
                # print "Disk: ", ElementTree.tostring(disk)
                # check if we can clone
                driver = disk.find("./driver[@name='qemu']")
                if driver is None:
                    # Err on the safe side
                    raise SaltCloudExecutionFailure("Non qemu driver disk encountered bailing out.")
                disk_type = driver.attrib.get('type')
                log.info("disk attributes {0}".format(disk.attrib))
                if disk_type == 'qcow2':
                    source = disk.find("./source").attrib['file']
                    pool, volume = find_pool_and_volume(conn, source)
                    if clone_strategy == 'quick':
                        new_volume = pool.createXML(create_volume_with_backing_store_xml(volume), 0)
                    else:
                        new_volume = pool.createXMLFrom(create_volume_xml(volume), volume, 0)
                    cleanup.append({'what': 'volume', 'item': new_volume})

                    disk.find("./source").attrib['file'] = new_volume.path()
                elif disk_type == 'raw':
                    source = disk.find("./source").attrib['file']
                    pool, volume = find_pool_and_volume(conn, source)
                    # TODO: more control on the cloned disk type
                    new_volume = pool.createXMLFrom(create_volume_xml(volume), volume, 0)
                    cleanup.append({'what': 'volume', 'item': new_volume})

                    disk.find("./source").attrib['file'] = new_volume.path()
                else:
                    raise SaltCloudExecutionFailure("Disk type '{0}' not supported".format(disk_type))

            log.debug("Clone XML '{0}'".format(domain_xml))
            clone_xml = ElementTree.tostring(domain_xml)

            clone_domain = conn.defineXMLFlags(clone_xml, libvirt.VIR_DOMAIN_DEFINE_VALIDATE)
            cleanup.append({'what': 'domain', 'item': clone_domain})
            clone_domain.createWithFlags(libvirt.VIR_DOMAIN_START_FORCE_BOOT)

        log.debug("VM '{0}'".format(vm_))

        if ip_source == 'qemu-agent':
            ip_source = libvirt.VIR_DOMAIN_INTERFACE_ADDRESSES_SRC_AGENT
        elif ip_source == 'ip-learning':
            ip_source = libvirt.VIR_DOMAIN_INTERFACE_ADDRESSES_SRC_LEASE

        address = salt.utils.cloud.wait_for_ip(
            get_domain_ip,
            update_args=(clone_domain, 0, ip_source),
            timeout=config.get_cloud_config_value('wait_for_ip_timeout', vm_, __opts__, default=10 * 60),
            interval=config.get_cloud_config_value('wait_for_ip_interval', vm_, __opts__, default=10),
            interval_multiplier=config.get_cloud_config_value('wait_for_ip_interval_multiplier', vm_, __opts__, default=1),
        )

        log.info('Address = {0}'.format(address))

        vm_['ssh_host'] = address

        # the bootstrap script needs to be installed first in /etc/salt/cloud.deploy.d/
        # salt-cloud -u is your friend
        ret = __utils__['cloud.bootstrap'](vm_, __opts__)

        __utils__['cloud.fire_event'](
            'event',
            'created instance',
            'salt/cloud/{0}/created'.format(name),
            args=__utils__['cloud.filter_event']('created', vm_, ['name', 'profile', 'provider', 'driver']),
            sock_dir=__opts__['sock_dir'],
            transport=__opts__['transport']
        )

        return ret
    except Exception as e:  # pylint: disable=broad-except
        # Try to clean up in as much cases as possible
        log.info('Cleaning up after exception clean up items: {0}'.format(cleanup))
        for leftover in cleanup:
            what = leftover['what']
            item = leftover['item']
            if what == 'domain':
                destroy_domain(conn, item)
            if what == 'volume':
                item.delete()
        raise e
Example #15
0
def create(vm_):
    '''
    Create a single VM from a data dict
    '''
    try:
        # Check for required profile parameters before sending any API calls.
        if vm_['profile'] and config.is_profile_configured(
                __opts__,
                __active_provider_name__ or 'opennebula',
                vm_['profile'],
                vm_=vm_) is False:
            return False
    except AttributeError:
        pass

    # Since using "provider: <provider-engine>" is deprecated, alias provider
    # to use driver: "driver: <provider-engine>"
    if 'provider' in vm_:
        vm_['driver'] = vm_.pop('provider')

    __utils__['cloud.fire_event'](
        'event',
        'starting create',
        'salt/cloud/{0}/creating'.format(vm_['name']),
        {
            'name': vm_['name'],
            'profile': vm_['profile'],
            'provider': vm_['driver'],
        },
    )

    log.info('Creating Cloud VM {0}'.format(vm_['name']))
    kwargs = {
        'name': vm_['name'],
        'image_id': get_image(vm_),
        'region_id': get_location(vm_),
    }

    private_networking = config.get_cloud_config_value('private_networking',
                                                       vm_,
                                                       __opts__,
                                                       search_global=False,
                                                       default=None)
    kwargs['private_networking'] = 'true' if private_networking else 'false'

    __utils__['cloud.fire_event'](
        'event',
        'requesting instance',
        'salt/cloud/{0}/requesting'.format(vm_['name']),
        {
            'kwargs': kwargs
        },
    )

    region = ''
    if kwargs['region_id'] is not None:
        region = 'SCHED_REQUIREMENTS="ID={0}"'.format(kwargs['region_id'])
    try:
        server, user, password = _get_xml_rpc()
        auth = ':'.join([user, password])
        server.one.template.instantiate(auth, int(kwargs['image_id']),
                                        kwargs['name'], False, region)
    except Exception as exc:
        log.error(
            'Error creating {0} on OpenNebula\n\n'
            'The following exception was thrown when trying to '
            'run the initial deployment: {1}'.format(vm_['name'], str(exc)),
            # Show the traceback if the debug logging level is enabled
            exc_info_on_loglevel=logging.DEBUG)
        return False

    def __query_node_data(vm_name):
        node_data = show_instance(vm_name, call='action')
        if not node_data:
            # Trigger an error in the wait_for_ip function
            return False
        if node_data['state'] == '7':
            return False
        if node_data['lcm_state'] == '3':
            return node_data

    try:
        data = __utils__['cloud.wait_for_ip'](
            __query_node_data,
            update_args=(vm_['name'], ),
            timeout=config.get_cloud_config_value('wait_for_ip_timeout',
                                                  vm_,
                                                  __opts__,
                                                  default=10 * 60),
            interval=config.get_cloud_config_value('wait_for_ip_interval',
                                                   vm_,
                                                   __opts__,
                                                   default=2),
        )
    except (SaltCloudExecutionTimeout, SaltCloudExecutionFailure) as exc:
        try:
            # It might be already up, let's destroy it!
            destroy(vm_['name'])
        except SaltCloudSystemExit:
            pass
        finally:
            raise SaltCloudSystemExit(str(exc))

    key_filename = config.get_cloud_config_value('private_key',
                                                 vm_,
                                                 __opts__,
                                                 search_global=False,
                                                 default=None)
    if key_filename is not None and not os.path.isfile(key_filename):
        raise SaltCloudConfigError(
            'The defined key_filename {0!r} does not exist'.format(
                key_filename))

    try:
        private_ip = data['private_ips'][0]
    except KeyError:
        private_ip = data['template']['nic']['ip']

    ssh_username = config.get_cloud_config_value('ssh_username',
                                                 vm_,
                                                 __opts__,
                                                 default='root')

    vm_['username'] = ssh_username
    vm_['key_filename'] = key_filename
    vm_['ssh_host'] = private_ip

    ret = __utils__['cloud.bootstrap'](vm_, __opts__)

    ret['id'] = data['id']
    ret['image'] = vm_['image']
    ret['name'] = vm_['name']
    ret['size'] = data['template']['memory']
    ret['state'] = data['state']
    ret['private_ips'] = private_ip
    ret['public_ips'] = []

    log.info('Created Cloud VM {0[name]!r}'.format(vm_))
    log.debug('{0[name]!r} VM creation details:\n{1}'.format(
        vm_, pprint.pformat(data)))

    __utils__['cloud.fire_event'](
        'event',
        'created instance',
        'salt/cloud/{0}/created'.format(vm_['name']),
        {
            'name': vm_['name'],
            'profile': vm_['profile'],
            'provider': vm_['driver'],
        },
    )

    return ret
Example #16
0
def request_instance(vm_=None, call=None):
    '''
    Put together all of the information necessary to request an instance
    through Novaclient and then fire off the request the instance.

    Returns data about the instance
    '''
    if call == 'function':
        # Technically this function may be called other ways too, but it
        # definitely cannot be called with --function.
        raise SaltCloudSystemExit(
            'The request_instance action must be called with -a or --action.')
    log.info('Creating Cloud VM %s', vm_['name'])
    salt.utils.cloud.check_name(vm_['name'], 'a-zA-Z0-9._-')
    conn = get_conn()
    kwargs = vm_.copy()

    try:
        kwargs['image_id'] = get_image(conn, vm_)
    except Exception as exc:
        raise SaltCloudSystemExit('Error creating {0} on OPENSTACK\n\n'
                                  'Could not find image {1}: {2}\n'.format(
                                      vm_['name'], vm_['image'], exc))

    try:
        kwargs['flavor_id'] = get_size(conn, vm_)
    except Exception as exc:
        raise SaltCloudSystemExit('Error creating {0} on OPENSTACK\n\n'
                                  'Could not find size {1}: {2}\n'.format(
                                      vm_['name'], vm_['size'], exc))

    kwargs['key_name'] = config.get_cloud_config_value('ssh_key_name',
                                                       vm_,
                                                       __opts__,
                                                       search_global=False)

    security_groups = config.get_cloud_config_value('security_groups',
                                                    vm_,
                                                    __opts__,
                                                    search_global=False)
    if security_groups is not None:
        vm_groups = security_groups
        avail_groups = conn.secgroup_list()
        group_list = []

        for vmg in vm_groups:
            if vmg in [name for name, details in six.iteritems(avail_groups)]:
                group_list.append(vmg)
            else:
                raise SaltCloudNotFound(
                    'No such security group: \'{0}\''.format(vmg))

        kwargs['security_groups'] = group_list

    avz = config.get_cloud_config_value('availability_zone',
                                        vm_,
                                        __opts__,
                                        default=None,
                                        search_global=False)
    if avz is not None:
        kwargs['availability_zone'] = avz

    kwargs['nics'] = config.get_cloud_config_value('networks',
                                                   vm_,
                                                   __opts__,
                                                   search_global=False,
                                                   default=None)

    files = config.get_cloud_config_value('files',
                                          vm_,
                                          __opts__,
                                          search_global=False)
    if files:
        kwargs['files'] = {}
        for src_path in files:
            if os.path.exists(files[src_path]):
                with salt.utils.files.fopen(files[src_path], 'r') as fp_:
                    kwargs['files'][src_path] = fp_.read()
            else:
                kwargs['files'][src_path] = files[src_path]

    userdata_file = config.get_cloud_config_value('userdata_file',
                                                  vm_,
                                                  __opts__,
                                                  search_global=False,
                                                  default=None)
    if userdata_file is not None:
        try:
            with salt.utils.files.fopen(userdata_file, 'r') as fp_:
                kwargs['userdata'] = salt.utils.cloud.userdata_template(
                    __opts__, vm_, fp_.read())
        except Exception as exc:
            log.exception('Failed to read userdata from %s: %s', userdata_file,
                          exc)

    kwargs['config_drive'] = config.get_cloud_config_value('config_drive',
                                                           vm_,
                                                           __opts__,
                                                           search_global=False)

    kwargs.update(get_block_mapping_opts(vm_))

    event_kwargs = {
        'name': kwargs['name'],
        'image': kwargs.get('image_id', 'Boot From Volume'),
        'size': kwargs['flavor_id'],
    }

    __utils__['cloud.fire_event'](
        'event',
        'requesting instance',
        'salt/cloud/{0}/requesting'.format(vm_['name']),
        args={
            'kwargs':
            __utils__['cloud.filter_event']('requesting', event_kwargs,
                                            list(event_kwargs)),
        },
        sock_dir=__opts__['sock_dir'],
        transport=__opts__['transport'])

    try:
        data = conn.boot(**kwargs)
    except Exception as exc:
        raise SaltCloudSystemExit(
            'Error creating {0} on Nova\n\n'
            'The following exception was thrown by libcloud when trying to '
            'run the initial deployment: {1}\n'.format(vm_['name'], exc))
    if data.extra.get('password', None) is None and vm_.get(
            'key_filename', None) is None:
        raise SaltCloudSystemExit('No password returned.  Set ssh_key_file.')

    floating_ip_conf = config.get_cloud_config_value('floating_ip',
                                                     vm_,
                                                     __opts__,
                                                     search_global=False,
                                                     default={})
    if floating_ip_conf.get('auto_assign', False):
        floating_ip = None
        if floating_ip_conf.get('ip_address', None) is not None:
            ip_address = floating_ip_conf.get('ip_address', None)
            try:
                fl_ip_dict = conn.floating_ip_show(ip_address)
                floating_ip = fl_ip_dict['ip']
            except Exception as err:
                raise SaltCloudSystemExit(
                    'Error assigning floating_ip for {0} on Nova\n\n'
                    'The following exception was thrown by libcloud when trying to '
                    'assign a floating ip: {1}\n'.format(vm_['name'], err))

        else:
            pool = floating_ip_conf.get('pool', 'public')
            try:
                floating_ip = conn.floating_ip_create(pool)['ip']
            except Exception:
                log.info(
                    'A new IP address was unable to be allocated. '
                    'An IP address will be pulled from the already allocated list, '
                    'This will cause a race condition when building in parallel.'
                )
                for fl_ip, opts in six.iteritems(conn.floating_ip_list()):
                    if opts['fixed_ip'] is None and opts['pool'] == pool:
                        floating_ip = fl_ip
                        break
                if floating_ip is None:
                    log.error(
                        'No IP addresses available to allocate for this server: %s',
                        vm_['name'])

        def __query_node_data(vm_):
            try:
                node = show_instance(vm_['name'], 'action')
                log.debug('Loaded node data for %s:\n%s', vm_['name'],
                          pprint.pformat(node))
            except Exception as err:
                log.error(
                    'Failed to get nodes list: %s',
                    err,
                    # Show the traceback if the debug logging level is enabled
                    exc_info_on_loglevel=logging.DEBUG)
                # Trigger a failure in the wait for IP function
                return False
            return node['state'] == 'ACTIVE' or None

        # if we associate the floating ip here,then we will fail.
        # As if we attempt to associate a floating IP before the Nova instance has completed building,
        # it will fail.So we should associate it after the Nova instance has completed building.
        try:
            salt.utils.cloud.wait_for_ip(__query_node_data,
                                         update_args=(vm_, ))
        except (SaltCloudExecutionTimeout, SaltCloudExecutionFailure) as exc:
            try:
                # It might be already up, let's destroy it!
                destroy(vm_['name'])
            except SaltCloudSystemExit:
                pass
            finally:
                raise SaltCloudSystemExit(six.text_type(exc))

        try:
            conn.floating_ip_associate(vm_['name'], floating_ip)
            vm_['floating_ip'] = floating_ip
        except Exception as exc:
            raise SaltCloudSystemExit(
                'Error assigning floating_ip for {0} on Nova\n\n'
                'The following exception was thrown by libcloud when trying to '
                'assign a floating ip: {1}\n'.format(vm_['name'], exc))

    if not vm_.get('password', None):
        vm_['password'] = data.extra.get('password', '')

    return data, vm_
Example #17
0
def create(vm_):
    '''
    Create a single VM from a data dict
    '''
    try:
        # Check for required profile parameters before sending any API calls.
        if vm_['profile'] and config.is_profile_configured(
                __opts__,
                __active_provider_name__ or 'dimensiondata',
                vm_['profile']) is False:
            return False
    except AttributeError:
        pass

    __utils__['cloud.fire_event'](
        'event',
        'starting create',
        'salt/cloud/{0}/creating'.format(vm_['name']),
        args=__utils__['cloud.filter_event']('creating', vm_, ['name', 'profile', 'provider', 'driver']),
        sock_dir=__opts__['sock_dir'],
        transport=__opts__['transport']
    )

    log.info('Creating Cloud VM %s', vm_['name'])
    conn = get_conn()
    rootPw = NodeAuthPassword(vm_['auth'])

    location = conn.ex_get_location_by_id(vm_['location'])
    images = conn.list_images(location=location)
    image = [x for x in images if x.id == vm_['image']][0]
    network_domains = conn.ex_list_network_domains(location=location)
    try:
        network_domain = [y for y in network_domains
                          if y.name == vm_['network_domain']][0]
    except IndexError:
        network_domain = conn.ex_create_network_domain(
            location=location,
            name=vm_['network_domain'],
            plan='ADVANCED',
            description=''
        )

    try:
        vlan = [y for y in conn.ex_list_vlans(
            location=location,
            network_domain=network_domain)
                if y.name == vm_['vlan']][0]
    except (IndexError, KeyError):
        # Use the first VLAN in the network domain
        vlan = conn.ex_list_vlans(
            location=location,
            network_domain=network_domain)[0]

    kwargs = {
        'name': vm_['name'],
        'image': image,
        'auth': rootPw,
        'ex_description': vm_['description'],
        'ex_network_domain': network_domain,
        'ex_vlan': vlan,
        'ex_is_started': vm_['is_started']
    }

    event_data = kwargs.copy()
    del event_data['auth']

    __utils__['cloud.fire_event'](
        'event',
        'requesting instance',
        'salt/cloud/{0}/requesting'.format(vm_['name']),
        args=__utils__['cloud.filter_event']('requesting', event_data, list(event_data)),
        sock_dir=__opts__['sock_dir'],
        transport=__opts__['transport']
    )

    try:
        data = conn.create_node(**kwargs)
    except Exception as exc:
        log.error(
            'Error creating %s on DIMENSIONDATA\n\n'
            'The following exception was thrown by libcloud when trying to '
            'run the initial deployment: \n%s',
            vm_['name'], exc,
            exc_info_on_loglevel=logging.DEBUG
        )
        return False

    try:
        data = salt.utils.cloud.wait_for_ip(
            _query_node_data,
            update_args=(vm_, data),
            timeout=config.get_cloud_config_value(
                'wait_for_ip_timeout', vm_, __opts__, default=25 * 60),
            interval=config.get_cloud_config_value(
                'wait_for_ip_interval', vm_, __opts__, default=30),
            max_failures=config.get_cloud_config_value(
                'wait_for_ip_max_failures', vm_, __opts__, default=60),
        )
    except (SaltCloudExecutionTimeout, SaltCloudExecutionFailure) as exc:
        try:
            # It might be already up, let's destroy it!
            destroy(vm_['name'])
        except SaltCloudSystemExit:
            pass
        finally:
            raise SaltCloudSystemExit(six.text_type(exc))

    log.debug('VM is now running')
    if ssh_interface(vm_) == 'private_ips':
        ip_address = preferred_ip(vm_, data.private_ips)
    else:
        ip_address = preferred_ip(vm_, data.public_ips)
    log.debug('Using IP address %s', ip_address)

    if salt.utils.cloud.get_salt_interface(vm_, __opts__) == 'private_ips':
        salt_ip_address = preferred_ip(vm_, data.private_ips)
        log.info('Salt interface set to: %s', salt_ip_address)
    else:
        salt_ip_address = preferred_ip(vm_, data.public_ips)
        log.debug('Salt interface set to: %s', salt_ip_address)

    if not ip_address:
        raise SaltCloudSystemExit(
            'No IP addresses could be found.'
        )

    vm_['salt_host'] = salt_ip_address
    vm_['ssh_host'] = ip_address
    vm_['password'] = vm_['auth']

    ret = salt.utils.cloud.bootstrap(vm_, __opts__)

    ret.update(data.__dict__)

    if 'password' in data.extra:
        del data.extra['password']

    log.info('Created Cloud VM \'%s\'', vm_['name'])
    log.debug(
        '\'%s\' VM creation details:\n%s',
        vm_['name'], pprint.pformat(data.__dict__)
    )

    __utils__['cloud.fire_event'](
        'event',
        'created instance',
        'salt/cloud/{0}/created'.format(vm_['name']),
        args=__utils__['cloud.filter_event']('created', vm_, ['name', 'profile', 'provider', 'driver']),
        sock_dir=__opts__['sock_dir'],
        transport=__opts__['transport']
    )

    return ret
Example #18
0
def create(vm_):
    '''
    Create a single VM from a data dict
    '''
    try:
        # Check for required profile parameters before sending any API calls.
        if vm_['profile'] and config.is_profile_configured(
                __opts__,
                __active_provider_name__ or 'nova',
                vm_['profile'],
                vm_=vm_) is False:
            return False
    except AttributeError:
        pass

    deploy = config.get_cloud_config_value('deploy', vm_, __opts__)
    key_filename = config.get_cloud_config_value('ssh_key_file',
                                                 vm_,
                                                 __opts__,
                                                 search_global=False,
                                                 default=None)
    if key_filename is not None and not os.path.isfile(key_filename):
        raise SaltCloudConfigError(
            'The defined ssh_key_file \'{0}\' does not exist'.format(
                key_filename))

    vm_['key_filename'] = key_filename

    __utils__['cloud.fire_event'](
        'event',
        'starting create',
        'salt/cloud/{0}/creating'.format(vm_['name']),
        args=__utils__['cloud.filter_event'](
            'creating', vm_, ['name', 'profile', 'provider', 'driver']),
        sock_dir=__opts__['sock_dir'],
        transport=__opts__['transport'])
    conn = get_conn()

    if 'instance_id' in vm_:
        # This was probably created via another process, and doesn't have
        # things like salt keys created yet, so let's create them now.
        if 'pub_key' not in vm_ and 'priv_key' not in vm_:
            log.debug('Generating minion keys for \'%s\'', vm_['name'])
            vm_['priv_key'], vm_['pub_key'] = salt.utils.cloud.gen_keys(
                salt.config.get_cloud_config_value('keysize', vm_, __opts__))
        data = conn.server_show_libcloud(vm_['instance_id'])
        if vm_['key_filename'] is None and 'change_password' in __opts__ and __opts__[
                'change_password'] is True:
            vm_['password'] = salt.utils.pycrypto.secure_password()
            conn.root_password(vm_['instance_id'], vm_['password'])
    else:
        # Put together all of the information required to request the instance,
        # and then fire off the request for it
        data, vm_ = request_instance(vm_)

        # Pull the instance ID, valid for both spot and normal instances
        vm_['instance_id'] = data.id

    try:
        data = salt.utils.cloud.wait_for_ip(
            _query_node_data,
            update_args=(vm_, data, conn),
            timeout=config.get_cloud_config_value('wait_for_ip_timeout',
                                                  vm_,
                                                  __opts__,
                                                  default=10 * 60),
            interval=config.get_cloud_config_value('wait_for_ip_interval',
                                                   vm_,
                                                   __opts__,
                                                   default=10),
        )
    except (SaltCloudExecutionTimeout, SaltCloudExecutionFailure) as exc:
        try:
            # It might be already up, let's destroy it!
            destroy(vm_['name'])
        except SaltCloudSystemExit:
            pass
        finally:
            raise SaltCloudSystemExit(six.text_type(exc))

    log.debug('VM is now running')

    if ssh_interface(vm_) == 'private_ips':
        ip_address = preferred_ip(vm_, data.private_ips)
    elif ssh_interface(vm_) == 'fixed_ips':
        ip_address = preferred_ip(vm_, data.fixed_ips)
    elif ssh_interface(vm_) == 'floating_ips':
        ip_address = preferred_ip(vm_, data.floating_ips)
    else:
        ip_address = preferred_ip(vm_, data.public_ips)
    log.debug('Using IP address %s', ip_address)

    if salt.utils.cloud.get_salt_interface(vm_, __opts__) == 'private_ips':
        salt_ip_address = preferred_ip(vm_, data.private_ips)
        log.info('Salt interface set to: %s', salt_ip_address)
    elif salt.utils.cloud.get_salt_interface(vm_, __opts__) == 'fixed_ips':
        salt_ip_address = preferred_ip(vm_, data.fixed_ips)
        log.info('Salt interface set to: %s', salt_ip_address)
    elif salt.utils.cloud.get_salt_interface(vm_, __opts__) == 'floating_ips':
        salt_ip_address = preferred_ip(vm_, data.floating_ips)
        log.info('Salt interface set to: %s', salt_ip_address)
    else:
        salt_ip_address = preferred_ip(vm_, data.public_ips)
        log.debug('Salt interface set to: %s', salt_ip_address)

    if not ip_address:
        raise SaltCloudSystemExit('A valid IP address was not found')

    vm_['ssh_host'] = ip_address
    vm_['salt_host'] = salt_ip_address

    ret = __utils__['cloud.bootstrap'](vm_, __opts__)

    ret.update(data.__dict__)

    if 'password' in ret['extra']:
        del ret['extra']['password']

    log.info('Created Cloud VM \'%s\'', vm_['name'])
    log.debug('\'%s\' VM creation details:\n%s', vm_['name'],
              pprint.pformat(data.__dict__))

    event_data = {
        'name': vm_['name'],
        'profile': vm_['profile'],
        'provider': vm_['driver'],
        'instance_id': vm_['instance_id'],
        'floating_ips': data.floating_ips,
        'fixed_ips': data.fixed_ips,
        'private_ips': data.private_ips,
        'public_ips': data.public_ips
    }

    __utils__['cloud.fire_event']('event',
                                  'created instance',
                                  'salt/cloud/{0}/created'.format(vm_['name']),
                                  args=__utils__['cloud.filter_event'](
                                      'created', event_data, list(event_data)),
                                  sock_dir=__opts__['sock_dir'],
                                  transport=__opts__['transport'])
    __utils__['cloud.cachedir_index_add'](vm_['name'], vm_['profile'], 'nova',
                                          vm_['driver'])
    return ret
Example #19
0
def create(vm_):
    '''
    Create a single VM from a data dict
    '''
    key_filename = config.get_cloud_config_value('private_key',
                                                 vm_,
                                                 __opts__,
                                                 search_global=False,
                                                 default=None)
    if key_filename is not None and not os.path.isfile(key_filename):
        raise SaltCloudConfigError(
            'The defined key_filename {0!r} does not exist'.format(
                key_filename))

    location = get_location(vm_)
    log.info('Creating Cloud VM {0} in {1}'.format(vm_['name'], location))
    conn = get_conn(location=location)
    usernames = ssh_username(vm_)
    kwargs = {
        'ssh_key':
        config.get_cloud_config_value('private_key',
                                      vm_,
                                      __opts__,
                                      search_global=False),
        'name':
        vm_['name'],
        'image':
        get_image(conn, vm_),
        'size':
        get_size(conn, vm_),
        'location':
        get_availability_zone(conn, vm_)
    }
    ex_keyname = keyname(vm_)
    if ex_keyname:
        kwargs['ex_keyname'] = ex_keyname
    ex_securitygroup = securitygroup(vm_)
    if ex_securitygroup:
        kwargs['ex_securitygroup'] = ex_securitygroup
    ex_blockdevicemappings = block_device_mappings(vm_)
    if ex_blockdevicemappings:
        kwargs['ex_blockdevicemappings'] = ex_blockdevicemappings

    ex_iam_profile = iam_profile(vm_)
    if ex_iam_profile:
        # libcloud does not implement 'iam_profile' yet.
        # A pull request has been suggested
        # https://github.com/apache/libcloud/pull/150
        raise SaltCloudConfigError(
            'libcloud does not implement \'iam_profile\' yet. '
            'Use EC2 driver instead.')

    tags = config.get_cloud_config_value('tag',
                                         vm_,
                                         __opts__, {},
                                         search_global=False)
    if not isinstance(tags, dict):
        raise SaltCloudConfigError('\'tag\' should be a dict.')
    kwargs['ex_metadata'] = config.get_cloud_config_value('metadata',
                                                          vm_,
                                                          __opts__,
                                                          default={},
                                                          search_global=False)
    if not isinstance(kwargs['ex_metadata'], dict):
        raise SaltCloudConfigError('\'metadata\' should be a dict.')

    try:
        data = conn.create_node(**kwargs)
    except Exception as exc:
        log.error(
            'Error creating {0} on AWS\n\n'
            'The following exception was thrown by libcloud when trying to '
            'run the initial deployment: {1}\n'.format(vm_['name'], exc),
            # Show the traceback if the debug logging level is enabled
            exc_info_on_loglevel=logging.DEBUG)
        return False

    log.info('Created node {0}'.format(vm_['name']))

    def __get_node_data(conn, vm_name):
        data = get_node(conn, vm_name)

        if data is None:
            # Trigger a failure in the waiting function
            return False

        if ssh_interface(vm_) == 'private_ips' and data.private_ips:
            return data

        if ssh_interface(vm_) == 'public_ips' and data.public_ips:
            return data

    try:
        data = salt.utils.cloud.wait_for_ip(
            __get_node_data,
            update_args=(conn, vm_['name']),
            timeout=config.get_cloud_config_value('wait_for_ip_timeout',
                                                  vm_,
                                                  __opts__,
                                                  default=5 * 60),
            interval=config.get_cloud_config_value('wait_for_ip_interval',
                                                   vm_,
                                                   __opts__,
                                                   default=0.5),
        )
    except (SaltCloudExecutionTimeout, SaltCloudExecutionFailure) as exc:
        try:
            # It might be already up, let's destroy it!
            destroy(vm_['name'])
        except SaltCloudSystemExit:
            pass
        finally:
            raise SaltCloudSystemExit(str(exc))

    if tags:
        set_tags(vm_['name'], tags, call='action')

    if ssh_interface(vm_) == 'private_ips':
        log.info('Salt node data. Private_ip: {0}'.format(data.private_ips[0]))
        ip_address = data.private_ips[0]
    else:
        log.info('Salt node data. Public_ip: {0}'.format(data.public_ips[0]))
        ip_address = data.public_ips[0]

    if salt.utils.cloud.get_salt_interface(vm_, __opts__) == 'private_ips':
        salt_ip_address = data.private_ips[0]
        log.info('Salt interface set to: {0}'.format(salt_ip_address))
    else:
        salt_ip_address = data.public_ips[0]
        log.debug('Salt interface set to: {0}'.format(salt_ip_address))

    username = '******'
    ssh_connect_timeout = config.get_cloud_config_value(
        'ssh_connect_timeout',
        vm_,
        __opts__,
        900  # 15 minutes
    )
    ssh_port = config.get_cloud_config_value('ssh_port', vm_, __opts__, 22)
    if salt.utils.cloud.wait_for_port(ip_address, timeout=ssh_connect_timeout):
        for user in usernames:
            if salt.utils.cloud.wait_for_passwd(
                    host=ip_address,
                    username=user,
                    ssh_timeout=config.get_cloud_config_value(
                        'wait_for_passwd_timeout',
                        vm_,
                        __opts__,
                        default=1 * 60),
                    key_filename=key_filename,
                    known_hosts_file=config.get_cloud_config_value(
                        'known_hosts_file', vm_, __opts__,
                        default='/dev/null'),
            ):
                username = user
                break
        else:
            raise SaltCloudSystemExit(
                'Failed to authenticate against remote ssh')

    ret = {}
    if config.get_cloud_config_value('deploy', vm_, __opts__) is True:
        deploy_script = script(vm_)
        deploy_kwargs = {
            'opts':
            __opts__,
            'host':
            ip_address,
            'port':
            ssh_port,
            'salt_host':
            salt_ip_address,
            'username':
            username,
            'key_filename':
            key_filename,
            'tmp_dir':
            config.get_cloud_config_value('tmp_dir',
                                          vm_,
                                          __opts__,
                                          default='/tmp/.saltcloud'),
            'deploy_command':
            config.get_cloud_config_value(
                'deploy_command',
                vm_,
                __opts__,
                default='/tmp/.saltcloud/deploy.sh',
            ),
            'tty':
            config.get_cloud_config_value('tty', vm_, __opts__, default=True),
            'script':
            deploy_script.script,
            'name':
            vm_['name'],
            'sudo':
            config.get_cloud_config_value('sudo',
                                          vm_,
                                          __opts__,
                                          default=(username != 'root')),
            'sudo_password':
            config.get_cloud_config_value('sudo_password',
                                          vm_,
                                          __opts__,
                                          default=None),
            'start_action':
            __opts__['start_action'],
            'parallel':
            __opts__['parallel'],
            'conf_file':
            __opts__['conf_file'],
            'sock_dir':
            __opts__['sock_dir'],
            'minion_pem':
            vm_['priv_key'],
            'minion_pub':
            vm_['pub_key'],
            'keep_tmp':
            __opts__['keep_tmp'],
            'preseed_minion_keys':
            vm_.get('preseed_minion_keys', None),
            'display_ssh_output':
            config.get_cloud_config_value('display_ssh_output',
                                          vm_,
                                          __opts__,
                                          default=True),
            'script_args':
            config.get_cloud_config_value('script_args', vm_, __opts__),
            'script_env':
            config.get_cloud_config_value('script_env', vm_, __opts__),
            'minion_conf':
            salt.utils.cloud.minion_config(__opts__, vm_)
        }

        # Deploy salt-master files, if necessary
        if config.get_cloud_config_value('make_master', vm_, __opts__) is True:
            deploy_kwargs['make_master'] = True
            deploy_kwargs['master_pub'] = vm_['master_pub']
            deploy_kwargs['master_pem'] = vm_['master_pem']
            master_conf = salt.utils.cloud.master_config(__opts__, vm_)
            deploy_kwargs['master_conf'] = master_conf

            if master_conf.get('syndic_master', None):
                deploy_kwargs['make_syndic'] = True

        deploy_kwargs['make_minion'] = config.get_cloud_config_value(
            'make_minion', vm_, __opts__, default=True)

        # Check for Windows install params
        win_installer = config.get_cloud_config_value('win_installer', vm_,
                                                      __opts__)
        if win_installer:
            deploy_kwargs['win_installer'] = win_installer
            minion = salt.utils.cloud.minion_config(__opts__, vm_)
            deploy_kwargs['master'] = minion['master']
            deploy_kwargs['username'] = config.get_cloud_config_value(
                'win_username', vm_, __opts__, default='Administrator')
            deploy_kwargs['password'] = config.get_cloud_config_value(
                'win_password', vm_, __opts__, default='')

        # Store what was used to the deploy the VM
        ret['deploy_kwargs'] = deploy_kwargs

        deployed = False
        if win_installer:
            deployed = salt.utils.cloud.deploy_windows(**deploy_kwargs)
        else:
            deployed = salt.utils.cloud.deploy_script(**deploy_kwargs)

        if deployed:
            log.info('Salt installed on {name}'.format(**vm_))
        else:
            log.error('Failed to start Salt on Cloud VM {name}'.format(**vm_))

    ret.update(data.__dict__)

    log.info('Created Cloud VM {0[name]!r}'.format(vm_))
    log.debug('{0[name]!r} VM creation details:\n{1}'.format(
        vm_, pprint.pformat(data.__dict__)))

    volumes = config.get_cloud_config_value('volumes',
                                            vm_,
                                            __opts__,
                                            search_global=True)
    if volumes:
        log.info('Create and attach volumes to node {0}'.format(data.name))
        create_attach_volumes(volumes, location, data)

    return ret
Example #20
0
def destroy(name, conn=None, call=None):
    '''
    Delete a single VM
    '''
    if call == 'function':
        raise SaltCloudSystemExit(
            'The destroy action must be called with -d, --destroy, '
            '-a or --action.'
        )

    __utils__['cloud.fire_event'](
        'event',
        'destroying instance',
        'salt/cloud/{0}/destroying'.format(name),
        args={'name': name},
        sock_dir=__opts__['sock_dir'],
        transport=__opts__['transport']
    )

    if not conn:
        conn = get_conn()   # pylint: disable=E0602

    node = get_node(conn, name)
    profiles = get_configured_provider()['profiles']  # pylint: disable=E0602
    if node is None:
        log.error('Unable to find the VM {0}'.format(name))
    profile = None
    if 'metadata' in node.extra and 'profile' in node.extra['metadata']:
        profile = node.extra['metadata']['profile']

    flush_mine_on_destroy = False
    if profile and profile in profiles and 'flush_mine_on_destroy' in profiles[profile]:
        flush_mine_on_destroy = profiles[profile]['flush_mine_on_destroy']

    if flush_mine_on_destroy:
        log.info('Clearing Salt Mine: {0}'.format(name))

        mopts_ = salt.config.DEFAULT_MINION_OPTS
        conf_path = '/'.join(__opts__['conf_file'].split('/')[:-1])
        mopts_.update(
            salt.config.minion_config(os.path.join(conf_path, 'minion'))
        )
        client = salt.client.get_local_client(mopts_)
        minions = client.cmd(name, 'mine.flush')

    log.info('Clearing Salt Mine: {0}, {1}'.format(name, flush_mine_on_destroy))
    log.info('Destroying VM: {0}'.format(name))
    ret = conn.destroy_node(node)
    if ret:
        log.info('Destroyed VM: {0}'.format(name))
        # Fire destroy action
        __utils__['cloud.fire_event'](
            'event',
            'destroyed instance',
            'salt/cloud/{0}/destroyed'.format(name),
            args={'name': name},
            sock_dir=__opts__['sock_dir'],
            transport=__opts__['transport']
        )
        if __opts__['delete_sshkeys'] is True:
            public_ips = getattr(node, __opts__.get('ssh_interface', 'public_ips'))
            if public_ips:
                salt.utils.cloud.remove_sshkey(public_ips[0])

            private_ips = getattr(node, __opts__.get('ssh_interface', 'private_ips'))
            if private_ips:
                salt.utils.cloud.remove_sshkey(private_ips[0])

        if __opts__.get('update_cachedir', False) is True:
            __utils__['cloud.delete_minion_cachedir'](name, __active_provider_name__.split(':')[0], __opts__)

        return True

    log.error('Failed to Destroy VM: {0}'.format(name))
    return False
Example #21
0
def get_entry(dict_, key, value):
    for entry in dict_:
        if entry[key] == value:
            return entry
    raise SaltCloudSystemExit('Unable to find {0} in {1}.'.format(key, dict_))
Example #22
0
def destroy(name, conn=None, call=None, kwargs=None):  # pylint: disable=unused-argument
    '''
    Destroy a VM

    CLI Examples:

    .. code-block:: bash

        salt-cloud -d myminion
        salt-cloud -a destroy myminion service_name=myservice
    '''
    if call == 'function':
        raise SaltCloudSystemExit(
            'The destroy action must be called with -d, --destroy, '
            '-a or --action.')

    global compconn  # pylint: disable=global-statement,invalid-name
    if not compconn:
        compconn = get_conn()

    if kwargs is None:
        kwargs = {}

    node_data = show_instance(name, call='action')
    vhd = node_data['storage_profile']['os_disk']['vhd']['uri']

    ret = {}
    log.debug('Deleting VM')
    result = compconn.virtual_machines.delete(node_data['resource_group'],
                                              name)
    result.wait()

    if __opts__.get('update_cachedir', False) is True:
        salt.utils.cloud.delete_minion_cachedir(
            name,
            __active_provider_name__.split(':')[0], __opts__)

    cleanup_disks = config.get_cloud_config_value(
        'cleanup_disks',
        get_configured_provider(),
        __opts__,
        search_global=False,
        default=False,
    )
    if cleanup_disks:
        cleanup_vhds = kwargs.get(
            'delete_vhd',
            config.get_cloud_config_value(
                'cleanup_vhds',
                get_configured_provider(),
                __opts__,
                search_global=False,
                default=False,
            ))
        if cleanup_vhds:
            log.debug('Deleting vhd')

        comps = vhd.split('.')
        container = comps[0].replace('https://', '')
        blob = node_data['storage_profile']['os_disk']['name']

        ret[name]['delete_disk'] = {
            'delete_vhd': cleanup_vhds,
            'container': container,
            'blob': blob,
        }
        #data = delete_disk(kwargs={'name': disk_name, 'delete_vhd': cleanup_vhds}, call='function')

    return ret
Example #23
0
def query(method='servers',
          server_id=None,
          command=None,
          args=None,
          http_method='GET',
          root='api_root'):
    ''' Make a call to the Scaleway API.
    '''

    if root == 'api_root':
        default_url = 'https://cp-par1.scaleway.com'
    else:
        default_url = 'https://api-marketplace.scaleway.com'

    base_path = six.text_type(
        config.get_cloud_config_value(root,
                                      get_configured_provider(),
                                      __opts__,
                                      search_global=False,
                                      default=default_url))

    path = '{0}/{1}/'.format(base_path, method)

    if server_id:
        path += '{0}/'.format(server_id)

    if command:
        path += command

    if not isinstance(args, dict):
        args = {}

    token = config.get_cloud_config_value('token',
                                          get_configured_provider(),
                                          __opts__,
                                          search_global=False)

    data = salt.utils.json.dumps(args)

    request = __utils__["http.query"](path,
                                      method=http_method,
                                      data=data,
                                      status=True,
                                      decode=True,
                                      decode_type='json',
                                      data_render=True,
                                      data_renderer='json',
                                      headers=True,
                                      header_dict={
                                          'X-Auth-Token': token,
                                          'User-Agent': "salt-cloud",
                                          'Content-Type': 'application/json'
                                      })
    if request['status'] > 299:
        raise SaltCloudSystemExit(
            'An error occurred while querying Scaleway. HTTP Code: {0}  '
            'Error: \'{1}\''.format(request['status'], request['error']))

    # success without data
    if request['status'] == 204:
        return True

    return salt.utils.json.loads(request['body'])
Example #24
0
def avail_images(conn=None, call=None):  # pylint: disable=unused-argument
    '''
    List available images for Azure
    '''
    if call == 'action':
        raise SaltCloudSystemExit(
            'The avail_images function must be called with '
            '-f or --function, or with the --list-images option')

    global compconn  # pylint: disable=global-statement,invalid-name
    if not compconn:
        compconn = get_conn()

    region = get_location()
    bank = 'cloud/metadata/azurearm/{0}'.format(region)
    publishers = _cache(
        bank,
        'publishers',
        compconn.virtual_machine_images.list_publishers,
        location=region,
    )

    ret = {}
    for publisher in publishers:
        pub_bank = os.path.join(bank, 'publishers', publisher)
        offers = _cache(
            pub_bank,
            'offers',
            compconn.virtual_machine_images.list_offers,
            location=region,
            publisher_name=publishers[publisher]['name'],
        )

        for offer in offers:
            offer_bank = os.path.join(pub_bank, 'offers', offer)
            skus = _cache(
                offer_bank,
                'skus',
                compconn.virtual_machine_images.list_skus,
                location=region,
                publisher_name=publishers[publisher]['name'],
                offer=offers[offer]['name'],
            )

            for sku in skus:
                sku_bank = os.path.join(offer_bank, 'skus', sku)
                results = _cache(
                    sku_bank,
                    'results',
                    compconn.virtual_machine_images.list,
                    location=region,
                    publisher_name=publishers[publisher]['name'],
                    offer=offers[offer]['name'],
                    skus=skus[sku]['name'],
                )

                for version in results:
                    name = '|'.join((
                        publishers[publisher]['name'],
                        offers[offer]['name'],
                        skus[sku]['name'],
                        results[version]['name'],
                    ))
                    ret[name] = {
                        'publisher': publishers[publisher]['name'],
                        'offer': offers[offer]['name'],
                        'sku': skus[sku]['name'],
                        'version': results[version]['name'],
                    }
    return ret
Example #25
0
def query(
    method="servers",
    server_id=None,
    command=None,
    args=None,
    http_method="GET",
    root="api_root",
):
    """ Make a call to the Scaleway API.
    """

    if root == "api_root":
        default_url = "https://cp-par1.scaleway.com"
    else:
        default_url = "https://api-marketplace.scaleway.com"

    vm_ = get_configured_provider()

    base_path = str(
        config.get_cloud_config_value(
            root,
            vm_,
            __opts__,
            search_global=False,
            default=default_url,
        ))

    path = "{}/{}/".format(base_path, method)

    if server_id:
        path += "{}/".format(server_id)

    if command:
        path += command

    if not isinstance(args, dict):
        args = {}

    token = config.get_cloud_config_value("token",
                                          vm_,
                                          __opts__,
                                          search_global=False)

    data = salt.utils.json.dumps(args)

    request = __utils__["http.query"](
        path,
        method=http_method,
        data=data,
        headers={
            "X-Auth-Token": token,
            "User-Agent": "salt-cloud",
            "Content-Type": "application/json",
        },
    )
    if request.status_code > 299:
        raise SaltCloudSystemExit(
            "An error occurred while querying Scaleway. HTTP Code: {}  "
            "Error: '{}'".format(request.status_code, request.text))

    # success without data
    if request["status"] == 204:
        return True

    return salt.utils.json.loads(request["body"])
Example #26
0
def query(params=None):
    '''
    Make a web call to QingCloud IaaS API.
    '''
    path = 'https://api.qingcloud.com/iaas/'

    access_key_id = config.get_cloud_config_value('access_key_id',
                                                  get_configured_provider(),
                                                  __opts__,
                                                  search_global=False)
    access_key_secret = config.get_cloud_config_value(
        'secret_access_key',
        get_configured_provider(),
        __opts__,
        search_global=False)

    # public interface parameters
    real_parameters = {
        'access_key_id': access_key_id,
        'signature_version': DEFAULT_QINGCLOUD_SIGNATURE_VERSION,
        'time_stamp': time.strftime('%Y-%m-%dT%H:%M:%SZ', time.gmtime()),
        'version': DEFAULT_QINGCLOUD_API_VERSION,
    }

    # include action or function parameters
    if params:
        for key, value in params.items():
            if isinstance(value, list):
                for i in range(1, len(value) + 1):
                    if isinstance(value[i - 1], dict):
                        for sk, sv in value[i - 1].items():
                            if isinstance(sv, dict) or isinstance(sv, list):
                                # sv = json_dump(sv)
                                sv = json.dumps(sv, separators=(',', ':'))
                            real_parameters['{0}.{1}.{2}'.format(key, i,
                                                                 sk)] = sv
                    else:
                        real_parameters['{0}.{1}'.format(key,
                                                         i)] = value[i - 1]
            else:
                real_parameters[key] = value

    # Calculate the string for Signature
    signature = _compute_signature(real_parameters, access_key_secret, 'GET',
                                   '/iaas/')
    real_parameters['signature'] = signature

    # print('parameters:')
    # pprint.pprint(real_parameters)

    request = requests.get(path, params=real_parameters, verify=False)

    # print('url:')
    # print(request.url)

    if request.status_code != 200:
        raise SaltCloudSystemExit(
            'An error occurred while querying QingCloud. HTTP Code: {0}  '
            'Error: {1!r}'.format(request.status_code, request.text))

    log.debug(request.url)

    content = request.text
    result = json.loads(content, object_hook=salt.utils.decode_dict)

    # print('response:')
    # pprint.pprint(result)

    if result['ret_code'] != 0:
        raise SaltCloudSystemExit(pprint.pformat(result.get('message', {})))

    return result
Example #27
0
    def __init__(self,
                 username,
                 project_id,
                 auth_url,
                 region_name=None,
                 password=None,
                 os_auth_plugin=None,
                 **kwargs):
        '''
        Set up nova credentials
        '''
        self.kwargs = kwargs.copy()
        if not self.extensions:
            if hasattr(OpenStackComputeShell, '_discover_extensions'):
                self.extensions = OpenStackComputeShell()._discover_extensions(
                    '2.0')
            else:
                self.extensions = client.discover_extensions('2.0')
            for extension in self.extensions:
                extension.run_hooks('__pre_parse_args__')
            self.kwargs['extensions'] = self.extensions

        self.kwargs['username'] = username
        self.kwargs['project_id'] = project_id
        self.kwargs['auth_url'] = auth_url
        self.kwargs['region_name'] = region_name
        self.kwargs['service_type'] = 'compute'

        # used in novaclient extensions to see if they are rackspace or not, to know if it needs to load
        # the hooks for that extension or not.  This is cleaned up by sanatize_novaclient
        self.kwargs['os_auth_url'] = auth_url

        if os_auth_plugin is not None:
            novaclient.auth_plugin.discover_auth_systems()
            auth_plugin = novaclient.auth_plugin.load_plugin(os_auth_plugin)
            self.kwargs['auth_plugin'] = auth_plugin
            self.kwargs['auth_system'] = os_auth_plugin

        if not self.kwargs.get('api_key', None):
            self.kwargs['api_key'] = password

        # This has to be run before sanatize_novaclient before extra variables are cleaned out.
        if hasattr(self, 'extensions'):
            # needs an object, not a dictionary
            self.kwargstruct = KwargsStruct(**self.kwargs)
            for extension in self.extensions:
                extension.run_hooks('__post_parse_args__', self.kwargstruct)
            self.kwargs = self.kwargstruct.__dict__

        self.kwargs = sanatize_novaclient(self.kwargs)

        # Requires novaclient version >= 2.6.1
        self.kwargs['version'] = str(kwargs.get('version', 2))

        conn = client.Client(**self.kwargs)
        try:
            conn.client.authenticate()
        except novaclient.exceptions.AmbiguousEndpoints:
            raise SaltCloudSystemExit(
                "Nova provider requires a 'region_name' to be specified")

        self.kwargs['auth_token'] = conn.client.auth_token
        self.catalog = conn.client.service_catalog.catalog['access'][
            'serviceCatalog']

        if region_name is not None:
            servers_endpoints = get_entry(self.catalog, 'type',
                                          'compute')['endpoints']
            self.kwargs['bypass_url'] = get_entry(servers_endpoints, 'region',
                                                  region_name)['publicURL']

        self.compute_conn = client.Client(**self.kwargs)

        volume_endpoints = get_entry(self.catalog,
                                     'type',
                                     'volume',
                                     raise_error=False).get('endpoints', {})
        if volume_endpoints:
            if region_name is not None:
                self.kwargs['bypass_url'] = get_entry(volume_endpoints,
                                                      'region',
                                                      region_name)['publicURL']

            self.volume_conn = client.Client(**self.kwargs)
            if hasattr(self, 'extensions'):
                self.expand_extensions()
        else:
            self.volume_conn = None
Example #28
0
def create(vm_):
    '''
    Create a single instance from a data dict.

    CLI Examples:

    .. code-block:: bash

        salt-cloud -p qingcloud-ubuntu-c1m1 hostname1
        salt-cloud -m /path/to/mymap.sls -P
    '''
    try:
        # Check for required profile parameters before sending any API calls.
        if config.is_profile_configured(
                __opts__, __active_provider_name__ or 'qingcloud',
                vm_['profile']) is False:
            return False
    except AttributeError:
        pass

    # Since using "provider: <provider-engine>" is deprecated, alias provider
    # to use driver: "driver: <provider-engine>"
    if 'provider' in vm_:
        vm_['driver'] = vm_.pop('provider')

    salt.utils.cloud.fire_event('event',
                                'starting create',
                                'salt/cloud/{0}/creating'.format(vm_['name']),
                                {
                                    'name': vm_['name'],
                                    'profile': vm_['profile'],
                                    'provider': vm_['driver'],
                                },
                                transport=__opts__['transport'])

    log.info('Creating Cloud VM {0}'.format(vm_['name']))

    # params
    params = {
        'action': 'RunInstances',
        'instance_name': vm_['name'],
        'zone': _get_location(vm_),
        'instance_type': _get_size(vm_),
        'image_id': _get_image(vm_),
        'vxnets.1': vm_['vxnets'],
        'login_mode': vm_['login_mode'],
        'login_keypair': vm_['login_keypair'],
    }

    salt.utils.cloud.fire_event('event',
                                'requesting instance',
                                'salt/cloud/{0}/requesting'.format(
                                    vm_['name']), {'kwargs': params},
                                transport=__opts__['transport'])

    result = query(params)
    new_instance_id = result['instances'][0]

    try:
        data = salt.utils.cloud.wait_for_ip(
            _query_node_data,
            update_args=(new_instance_id, ),
            timeout=config.get_cloud_config_value('wait_for_ip_timeout',
                                                  vm_,
                                                  __opts__,
                                                  default=10 * 60),
            interval=config.get_cloud_config_value('wait_for_ip_interval',
                                                   vm_,
                                                   __opts__,
                                                   default=10),
        )
    except (SaltCloudExecutionTimeout, SaltCloudExecutionFailure) as exc:
        try:
            # It might be already up, let's destroy it!
            destroy(vm_['name'])
        except SaltCloudSystemExit:
            pass
        finally:
            raise SaltCloudSystemExit(str(exc))

    private_ip = data['private_ips'][0]

    log.debug('VM {0} is now running'.format(private_ip))

    vm_['ssh_host'] = private_ip

    # The instance is booted and accessible, let's Salt it!
    salt.utils.cloud.bootstrap(vm_, __opts__)

    log.info('Created Cloud VM {0[name]!r}'.format(vm_))

    log.debug('{0[name]!r} VM creation details:\n{1}'.format(
        vm_, pprint.pformat(data)))

    salt.utils.cloud.fire_event('event',
                                'created instance',
                                'salt/cloud/{0}/created'.format(vm_['name']), {
                                    'name': vm_['name'],
                                    'profile': vm_['profile'],
                                    'provider': vm_['driver'],
                                },
                                transport=__opts__['transport'])

    return data
Example #29
0
def create(vm_):
    '''
    Create a single VM from a data dict
    '''
    try:
        # Check for required profile parameters before sending any API calls.
        if vm_['profile'] and config.is_profile_configured(__opts__,
                                                           __active_provider_name__ or 'digital_ocean',
                                                           vm_['profile']) is False:
            return False
    except AttributeError:
        pass

    # Since using "provider: <provider-engine>" is deprecated, alias provider
    # to use driver: "driver: <provider-engine>"
    if 'provider' in vm_:
        vm_['driver'] = vm_.pop('provider')

    salt.utils.cloud.fire_event(
        'event',
        'starting create',
        'salt/cloud/{0}/creating'.format(vm_['name']),
        {
            'name': vm_['name'],
            'profile': vm_['profile'],
            'provider': vm_['driver'],
        },
        transport=__opts__['transport']
    )

    log.info('Creating Cloud VM {0}'.format(vm_['name']))

    kwargs = {
        'name': vm_['name'],
        'size': get_size(vm_),
        'image': get_image(vm_),
        'region': get_location(vm_),
        'ssh_keys': []
    }

    # backwards compat
    ssh_key_name = config.get_cloud_config_value(
        'ssh_key_name', vm_, __opts__, search_global=False
    )

    if ssh_key_name:
        kwargs['ssh_keys'].append(get_keyid(ssh_key_name))

    ssh_key_names = config.get_cloud_config_value(
        'ssh_key_names', vm_, __opts__, search_global=False, default=False
    )

    if ssh_key_names:
        for key in ssh_key_names.split(','):
            kwargs['ssh_keys'].append(get_keyid(key))

    key_filename = config.get_cloud_config_value(
        'ssh_key_file', vm_, __opts__, search_global=False, default=None
    )

    if key_filename is not None and not os.path.isfile(key_filename):
        raise SaltCloudConfigError(
            'The defined key_filename {0!r} does not exist'.format(
                key_filename
            )
        )

    if key_filename is None:
        raise SaltCloudConfigError(
            'The DigitalOcean driver requires an ssh_key_file and an ssh_key_name '
            'because it does not supply a root password upon building the server.'
        )

    private_networking = config.get_cloud_config_value(
        'private_networking', vm_, __opts__, search_global=False, default=None,
    )

    if private_networking is not None:
        if not isinstance(private_networking, bool):
            raise SaltCloudConfigError("'private_networking' should be a boolean value.")
        kwargs['private_networking'] = private_networking

    backups_enabled = config.get_cloud_config_value(
        'backups_enabled', vm_, __opts__, search_global=False, default=None,
    )

    if backups_enabled is not None:
        if not isinstance(backups_enabled, bool):
            raise SaltCloudConfigError("'backups_enabled' should be a boolean value.")
        kwargs['backups'] = backups_enabled

    ipv6 = config.get_cloud_config_value(
        'ipv6', vm_, __opts__, search_global=False, default=None,
    )

    if ipv6 is not None:
        if not isinstance(ipv6, bool):
            raise SaltCloudConfigError("'ipv6' should be a boolean value.")
        kwargs['ipv6'] = ipv6

    salt.utils.cloud.fire_event(
        'event',
        'requesting instance',
        'salt/cloud/{0}/requesting'.format(vm_['name']),
        {'kwargs': kwargs},
        transport=__opts__['transport']
    )

    try:
        ret = create_node(kwargs)
    except Exception as exc:
        log.error(
            'Error creating {0} on DIGITAL_OCEAN\n\n'
            'The following exception was thrown when trying to '
            'run the initial deployment: {1}'.format(
                vm_['name'],
                str(exc)
            ),
            # Show the traceback if the debug logging level is enabled
            exc_info_on_loglevel=logging.DEBUG
        )
        return False

    def __query_node_data(vm_name):
        data = show_instance(vm_name, 'action')
        if not data:
            # Trigger an error in the wait_for_ip function
            return False
        if data['networks'].get('v4'):
            for network in data['networks']['v4']:
                if network['type'] == 'public':
                    return data
        return False

    try:
        data = salt.utils.cloud.wait_for_ip(
            __query_node_data,
            update_args=(vm_['name'],),
            timeout=config.get_cloud_config_value(
                'wait_for_ip_timeout', vm_, __opts__, default=10 * 60),
            interval=config.get_cloud_config_value(
                'wait_for_ip_interval', vm_, __opts__, default=10),
        )
    except (SaltCloudExecutionTimeout, SaltCloudExecutionFailure) as exc:
        try:
            # It might be already up, let's destroy it!
            destroy(vm_['name'])
        except SaltCloudSystemExit:
            pass
        finally:
            raise SaltCloudSystemExit(str(exc))

    for network in data['networks']['v4']:
        if network['type'] == 'public':
            ip_address = network['ip_address']
    vm_['key_filename'] = key_filename
    vm_['ssh_host'] = ip_address
    ret = salt.utils.cloud.bootstrap(vm_, __opts__)
    ret.update(data)

    log.info('Created Cloud VM {0[name]!r}'.format(vm_))
    log.debug(
        '{0[name]!r} VM creation details:\n{1}'.format(
            vm_, pprint.pformat(data)
        )
    )

    salt.utils.cloud.fire_event(
        'event',
        'created instance',
        'salt/cloud/{0}/created'.format(vm_['name']),
        {
            'name': vm_['name'],
            'profile': vm_['profile'],
            'provider': vm_['driver'],
        },
        transport=__opts__['transport']
    )

    return ret
Example #30
0
def query(method='droplets',
          droplet_id=None,
          command=None,
          args=None,
          http_method='get'):
    '''
    Make a web call to DigitalOcean
    '''
    base_path = str(
        config.get_cloud_config_value(
            'api_root',
            get_configured_provider(),
            __opts__,
            search_global=False,
            default='https://api.digitalocean.com/v2'))

    path = '{0}/{1}/'.format(base_path, method)

    if droplet_id:
        path += '{0}/'.format(droplet_id)

    if command:
        path += command

    if not isinstance(args, dict):
        args = {}

    personal_access_token = config.get_cloud_config_value(
        'personal_access_token',
        get_configured_provider(),
        __opts__,
        search_global=False)

    data = json.dumps(args)

    requester = getattr(requests, http_method)
    request = requester(path,
                        data=data,
                        headers={
                            'Authorization': 'Bearer ' + personal_access_token,
                            'Content-Type': 'application/json'
                        })
    if request.status_code > 299:
        raise SaltCloudSystemExit(
            'An error occurred while querying DigitalOcean. HTTP Code: {0}  '
            'Error: \'{1}\''.format(
                request.status_code,
                # request.read()
                request.text))

    log.debug(request.url)

    # success without data
    if request.status_code == 204:
        return True

    content = request.text

    result = json.loads(content)
    if result.get('status', '').lower() == 'error':
        raise SaltCloudSystemExit(
            pprint.pformat(result.get('error_message', {})))

    return result