Beispiel #1
0
 def __init__(self, api_key=None, url=maas_url):
     if api_key == None:
         self.api_key = self.get_api_key()
     else:
         self.api_key = api_key
     self.auth = MAASOAuth(*self.api_key.split(':'))
     self.url = url
     self.client = MAASClient(self.auth, MAASDispatcher(), self.url)
Beispiel #2
0
def create_node(mac, arch, power_type, power_parameters):
    api_credentials = get_recorded_api_credentials()
    if api_credentials is None:
        raise Exception('Not creating node: no API key yet.')
    client = MAASClient(MAASOAuth(*api_credentials), MAASDispatcher(),
                        get_maas_url())

    data = {
        'op': 'new',
        'architecture': arch,
        'power_type': power_type,
        'power_parameters': power_parameters,
        'mac_addresses': mac
    }
    return client.post('/api/1.0/nodes/', data)
Beispiel #3
0
def make_client(root=None, result=None):
    """Create a MAASClient."""
    if root is None:
        root = make_url()
    auth = MAASOAuth(factory.getRandomString(), factory.getRandomString(),
                     factory.getRandomString())
    return MAASClient(auth, FakeDispatcher(result=result), root)
Beispiel #4
0
def evaluate_tag(
    system_id,
    nodes,
    tag_name,
    tag_definition,
    tag_nsmap,
    credentials,
    maas_url,
):
    """Evaluate `tag_definition` against this cluster's nodes' details.

    :param system_id: System ID for the rack controller.
    :param nodes: List of nodes to evaluate.
    :param tag_name: The name of the tag, used for logging.
    :param tag_definition: The XPath expression of the tag.
    :param tag_nsmap: The namespace map as used by LXML's ETree library.
    :param credentials: A 3-tuple of OAuth credentials.
    :param maas_url: URL of the MAAS API.
    """
    # Turn off proxy detection, since the rack should talk directly to
    # the region, even if a system-wide proxy is configured.
    client = MAASClient(
        auth=MAASOAuth(*credentials),
        dispatcher=MAASDispatcher(autodetect_proxies=False),
        base_url=maas_url,
    )
    process_node_tags(
        rack_id=system_id,
        nodes=nodes,
        tag_name=tag_name,
        tag_definition=tag_definition,
        tag_nsmap=tag_nsmap,
        client=client,
    )
Beispiel #5
0
def create_node(mac, arch, power_type, power_parameters):
    api_credentials = get_recorded_api_credentials()
    if api_credentials is None:
        raise Exception('Not creating node: no API key yet.')
    client = MAASClient(
        MAASOAuth(*api_credentials), MAASDispatcher(),
        get_maas_url())

    data = {
        'op': 'new',
        'architecture': arch,
        'power_type': power_type,
        'power_parameters': power_parameters,
        'mac_addresses': mac
    }
    return client.post('/api/1.0/nodes/', data)
Beispiel #6
0
def evaluate_tag(
    system_id,
    nodes,
    tag_name,
    tag_definition,
    tag_nsmap,
    credentials,
    maas_url,
):
    """Evaluate `tag_definition` against this cluster's nodes' details.

    :param system_id: System ID for the rack controller.
    :param nodes: List of nodes to evaluate.
    :param tag_name: The name of the tag, used for logging.
    :param tag_definition: The XPath expression of the tag.
    :param tag_nsmap: The namespace map as used by LXML's ETree library.
    :param credentials: A 3-tuple of OAuth credentials.
    :param maas_url: URL of the MAAS API.
    """
    client = MAASClient(
        auth=MAASOAuth(*credentials),
        dispatcher=MAASDispatcher(),
        base_url=maas_url,
    )
    process_node_tags(
        rack_id=system_id,
        nodes=nodes,
        tag_name=tag_name,
        tag_definition=tag_definition,
        tag_nsmap=tag_nsmap,
        client=client,
    )
Beispiel #7
0
def submit(maas_url, api_credentials, images):
    """Submit images to server."""
    MAASClient(MAASOAuth(*api_credentials), MAASDispatcher(),
               maas_url).post('api/1.0/boot-images/',
                              'report_boot_images',
                              nodegroup=get_cluster_uuid(),
                              images=json.dumps(images))
Beispiel #8
0
def make_client(root=None, result=None):
    """Create a MAASClient."""
    if root is None:
        root = factory.make_simple_http_url(path=factory.make_name("path") +
                                            "/")
    auth = MAASOAuth(factory.make_string(), factory.make_string(),
                     factory.make_string())
    return MAASClient(auth, FakeDispatcher(result=result), root)
Beispiel #9
0
def update_region_controller(knowledge, interface, server):
    """Update the region controller with the status of the probe.

    :param knowledge: dictionary of server info
    :param interface: name of interface, e.g. eth0
    :param server: IP address of detected DHCP server, or None
    """
    api_path = 'api/1.0/nodegroups/%s/interfaces/%s/' % (
        knowledge['nodegroup_uuid'], interface)
    oauth = MAASOAuth(*knowledge['api_credentials'])
    client = MAASClient(oauth, MAASDispatcher(), knowledge['maas_url'])
    if server is None:
        server = ''
    process_request(client.put, api_path, foreign_dhcp_ip=server)
Beispiel #10
0
def get_cached_knowledge():
    """Get all the information that we need to know, or raise an error.

    :return: (client, nodegroup_uuid)
    """
    api_credentials = get_recorded_api_credentials()
    if api_credentials is None:
        logger.error("Not updating tags: don't have API key yet.")
        return None, None
    nodegroup_uuid = get_recorded_nodegroup_uuid()
    if nodegroup_uuid is None:
        logger.error("Not updating tags: don't have UUID yet.")
        return None, None
    client = MAASClient(MAASOAuth(*api_credentials), MAASDispatcher(),
                        get_maas_url())
    return client, nodegroup_uuid
Beispiel #11
0
def _getclient(url=u'http://localhost/MAAS/api/1.0/'):
    '''
  Use the MAAS apiclient to aquire a session with the Maas API
  :param url:
    How to connect to the Maas server. As we require the Maas tools
    installed on the executing machine, this can be the localhost by default.

  :return:
    A MAASClient object
  '''
    global _mclient
    if _mclient is None:
        consumer_key, token, secret = key('root').split(':', 3)
        auth = MAASOAuth(consumer_key, token, secret)
        dispatch = MAASDispatcher()
        _mclient = MAASClient(auth, dispatch, url)

    return _mclient
Beispiel #12
0
class MaasTagging(object):
    def __init__(self):
        """ Main execution path """
        self.conn = None
        self.read_settings()
        self.run()

    def _connect(self):
        if not self.conn:
            auth = MAASOAuth(*self.maas_api_key.split(':'))
            dispatcher = MAASDispatcher()
            self.maas_client = MAASClient(auth, dispatcher, self.maas_url)
            self.conn = True

    def read_settings(self):
        """ Reads the settings from the maansible.ini file """

        config = ConfigParser.SafeConfigParser()
        config.read(
            os.path.dirname(os.path.realpath(__file__)) + '/maansible.ini')

        self.maas_url = config.get('maas', 'url')
        self.maas_api_key = config.get('maas', 'api_key')

        self.tags = dict(config.items('tags'))

    def run(self):
        """ Make calls to Maas to create tags and assign nodes to them """

        self._connect()

        # Get system ids of nodes
        nodes = self.maas_client.get('/nodes/', 'list').read()
        system_ids = {}
        for node in json.loads(nodes):
            system_ids[node['hostname'].split('.')[0]] = node['system_id']

        # Get existing tags
        tags = self.maas_client.get('/tags/', 'list').read()
        tags = [tag['name'] for tag in json.loads(tags)]

        # Create tags if not existing and apply them
        for tag in self.tags:
            if not tag in tags:
                params = {'name': tag}
                self.maas_client.post('/tags/', 'new', **params)
            nodes = self.tags[tag].split(',')
            for node in nodes:
                params = {'add': system_ids[node]}
                self.maas_client.post('/tags/%s/' % tag, 'update_nodes',
                                      **params)
class MaasTagging(object):

    def __init__(self):
        """ Main execution path """
        self.conn = None
        self.read_settings()
        self.run()

    def _connect(self):
        if not self.conn:
            auth = MAASOAuth(*self.maas_api_key.split(':'))
            dispatcher = MAASDispatcher()
            self.maas_client = MAASClient(auth, dispatcher, self.maas_url)
            self.conn = True

    def read_settings(self):
        """ Reads the settings from the maansible.ini file """

        config = ConfigParser.SafeConfigParser()
        config.read(os.path.dirname(os.path.realpath(__file__)) + '/maansible.ini')

        self.maas_url = config.get('maas', 'url')
        self.maas_api_key = config.get('maas', 'api_key')

	self.tags = dict(config.items('tags'))

    def run(self):
        """ Make calls to Maas to create tags and assign nodes to them """

        self._connect()

        # Get system ids of nodes
        nodes = self.maas_client.get('/nodes/', 'list').read()
        system_ids = {}
        for node in json.loads(nodes):
            system_ids[node['hostname'].split('.')[0]] = node['system_id']

        # Get existing tags
        tags = self.maas_client.get('/tags/', 'list').read()
        tags = [tag['name'] for tag in json.loads(tags)]

        # Create tags if not existing and apply them
        for tag in self.tags:
            if not tag in tags:
                params = {'name': tag}
                self.maas_client.post('/tags/', 'new', **params)
            nodes = self.tags[tag].split(',')
            for node in nodes:
                params = {'add': system_ids[node]}
                self.maas_client.post('/tags/%s/' % tag, 'update_nodes', **params)
Beispiel #14
0
def determine_cluster_interfaces(knowledge):
    """Given server knowledge, determine network interfaces on this cluster.

    :return: a list of tuples of (interface name, ip) for all interfaces.

    :note: this uses an API call and not local probing because the
        region controller has the definitive and final say in what does and
        doesn't exist.
    """
    api_path = 'api/1.0/nodegroups/%s/interfaces' % knowledge['nodegroup_uuid']
    oauth = MAASOAuth(*knowledge['api_credentials'])
    client = MAASClient(oauth, MAASDispatcher(), knowledge['maas_url'])
    interfaces = process_request(client.get, api_path, 'list')
    if interfaces is None:
        return None

    interface_names = sorted((interface['interface'], interface['ip'])
                             for interface in interfaces
                             if interface['interface'] != '')
    return interface_names
Beispiel #15
0
def send_leases(leases):
    """Send lease updates to the server API."""
    # Items that the server must have sent us before we can do this.
    knowledge = {
        'maas_url': get_maas_url(),
        'api_credentials': get_recorded_api_credentials(),
        'nodegroup_uuid': get_recorded_nodegroup_uuid(),
    }
    if None in knowledge.values():
        # The MAAS server hasn't sent us enough information for us to do
        # this yet.  Leave it for another time.
        logger.info(
            "Not sending DHCP leases to server: not all required knowledge "
            "received from server yet.  "
            "Missing: %s" % ', '.join(list_missing_items(knowledge)))
        return

    api_path = 'api/1.0/nodegroups/%s/' % knowledge['nodegroup_uuid']
    oauth = MAASOAuth(*knowledge['api_credentials'])
    MAASClient(oauth, MAASDispatcher(),
               knowledge['maas_url']).post(api_path,
                                           'update_leases',
                                           leases=json.dumps(leases))
Beispiel #16
0
class FabricMAAS(object):
    CORD_TEST_HOST = '172.17.0.1'
    head_node = os.getenv('HEAD_NODE', CORD_TEST_HOST)
    maas_url = 'http://{}/MAAS/api/1.0/'.format(head_node)

    def __init__(self, api_key=None, url=maas_url):
        if api_key == None:
            self.api_key = self.get_api_key()
        else:
            self.api_key = api_key
        self.auth = MAASOAuth(*self.api_key.split(':'))
        self.url = url
        self.client = MAASClient(self.auth, MAASDispatcher(), self.url)

    @classmethod
    def get_api_key(cls):
        api_key = os.getenv('MAAS_API_KEY', None)
        if api_key:
            return api_key
        cmd = ['maas-region-admin', 'apikey', '--username=cord']
        try:
            p = subprocess.Popen(cmd,
                                 stdin=subprocess.PIPE,
                                 stdout=subprocess.PIPE,
                                 stderr=subprocess.PIPE)
        except:
            return 'UNKNOWN'
        out, err = p.communicate()
        if err:
            raise Exception('Cannot get api key for MAAS')
        return out.strip()

    def get_node_list(self):
        nodes = self.client.get(u'nodes/', 'list').read()
        node_list = json.loads(nodes)
        hosts = [self.head_node] + map(lambda n: n['hostname'], node_list)
        return hosts
Beispiel #17
0
def _auth(**connection_args):
    '''
    Set up maas credentials

    Only intended to be used within maas-enabled modules
    '''

    prefix = "maas."

    # look in connection_args first, then default to config file
    def get(key, default=None):
        return connection_args.get(
            'connection_' + key, __salt__['config.get'](prefix + key, default))

    api_token = get('token')
    api_url = get('url', 'https://localhost/')

    LOG.debug("MAAS url: " + api_url)
    LOG.debug("MAAS token: " + api_token)
    auth = MAASOAuth(*api_token.split(":"))
    dispatcher = MAASDispatcher()
    client = MAASClient(auth, dispatcher, api_url)

    return client
 def _connect(self):
     if not self.conn:
         auth = MAASOAuth(*self.maas_api_key.split(':'))
         dispatcher = MAASDispatcher()
         self.maas_client = MAASClient(auth, dispatcher, self.maas_url)
         self.conn = True
Beispiel #19
0
 def fake_client(self):
     return MAASClient(None, None, self.make_maas_url())
Beispiel #20
0
 def fake_client(self):
     return MAASClient(None, None, factory.make_simple_http_url())
class MaasInventory(object):

    def __init__(self):
        """ Main execution path """
        self.conn = None

        self.inventory = dict()  # A list of groups and the hosts in that group
        self.cache = dict()  # Details about hosts in the inventory

        # Read settings and parse CLI arguments
        self.read_settings()
        self.parse_cli_args()

        # Cache
        if self.args.refresh_cache:
            self.update_cache()
        elif not self.is_cache_valid():
            self.update_cache()
        else:
            self.load_inventory_from_cache()
            self.load_cache_from_cache()

        data_to_print = ""

        # Data to print
        if self.args.host:
            data_to_print = self.get_host_info()

        elif self.args.list:
            # Display list of instances for inventory
            data_to_print = self.json_format_dict(self.inventory, True)

        else:  # default action with no options
            data_to_print = self.json_format_dict(self.inventory, True)

        print data_to_print

    def _connect(self):
        if not self.conn:
            auth = MAASOAuth(*self.maas_api_key.split(':'))
            dispatcher = MAASDispatcher()
            self.maas_client = MAASClient(auth, dispatcher, self.maas_url)
            self.conn = True

    def is_cache_valid(self):
        """ Determines if the cache files have expired, or if it is still valid """

        if os.path.isfile(self.cache_path_cache):
            mod_time = os.path.getmtime(self.cache_path_cache)
            current_time = time()
            if (mod_time + self.cache_max_age) > current_time:
                if os.path.isfile(self.cache_path_inventory):
                    return True

        return False

    def read_settings(self):
        """ Reads the settings from the maansible.ini file """

        config = ConfigParser.SafeConfigParser()
        config.read(os.path.dirname(os.path.realpath(__file__)) + '/maansible.ini')

        self.maas_url = config.get('maas', 'url')
        self.maas_api_key = config.get('maas', 'api_key')

        self.tags = dict(config.items('tags'))

        family = dict(config.items('family'))
        groups = []
        for parent in family:
            group = {
                'name': parent,
                'children': family[parent].split(','),
                'vars': dict(config.items(parent+':vars'))
            }
            groups.append(group)
        self.ansible_groups = groups

        host_vars = dict(config.items('host:vars'))
        host_vars['devices'] = host_vars['devices'].split(',')
        host_vars['raw_journal_devices'] = host_vars['raw_journal_devices'].split(',')
        self.ansible_host_vars = host_vars

        # Cache related
        cache_path = config.get('maas', 'cache_path')
        self.cache_path_cache = cache_path + "/ansible-maas.cache"
        self.cache_path_inventory = cache_path + "/ansible-maas.index"
        self.cache_max_age = config.getint('maas', 'cache_max_age')

    def parse_cli_args(self):
        """ Command line argument processing """

        parser = argparse.ArgumentParser(description='Produce an Ansible Inventory file based on Maas')
        parser.add_argument('--list', action='store_true', default=True, help='List instances (default: True)')
        parser.add_argument('--host', action='store', help='Get all the variables about a specific instance')
        parser.add_argument('--refresh-cache', action='store_true', default=False,
                            help='Force refresh of cache by making API requests to Maas (default: False - use cache files)')
        self.args = parser.parse_args()

    def update_cache(self):
        """ Make calls to Maas and save the output in a cache """

        self._connect()
        self.groups = dict()
        self.hosts = dict()

        hostvars = dict()
        for tag in self.tags:
            nodes = self.maas_client.get('/tags/' + tag, 'nodes').read()
            nodes = json.loads(nodes)
            hosts = []
            for node in nodes:
                hostname = node['hostname']
                ip_address = node['ip_addresses'][0]
                if not hostname in hostvars:
                    hostvars[hostname] = dict()
                    hostvars[hostname].update(self.ansible_host_vars)
                    hostvars[hostname]['ansible_ssh_host'] = ip_address
                hosts.append(hostname)
            self.inventory[tag] = {'hosts': hosts}
        self.inventory['_meta'] = {'hostvars': hostvars}

        for group in self.ansible_groups:
            self.inventory[group['name']] = {
                'children': group['children'],
                'vars': group['vars']
            }

        self.write_to_cache(self.cache, self.cache_path_cache)
        self.write_to_cache(self.inventory, self.cache_path_inventory)

    def get_host_info(self):
        """ Get variables about a specific host """

        if not self.cache or len(self.cache) == 0:
            # Need to load index from cache
            self.load_cache_from_cache()

        if not self.args.host in self.cache:
            # try updating the cache
            self.update_cache()

            if not self.args.host in self.cache:
                # host might not exist anymore
                return self.json_format_dict({}, True)

        return self.json_format_dict(self.cache[self.args.host], True)

    def push(self, my_dict, key, element):
        """ Pushed an element onto an array that may not have been defined in the dict """

        if key in my_dict:
            my_dict[key].append(element)
        else:
            my_dict[key] = [element]

    def load_inventory_from_cache(self):
        """ Reads the index from the cache file sets self.index """

        cache = open(self.cache_path_inventory, 'r')
        json_inventory = cache.read()
        self.inventory = json.loads(json_inventory)

    def load_cache_from_cache(self):
        """ Reads the cache from the cache file sets self.cache """

        cache = open(self.cache_path_cache, 'r')
        json_cache = cache.read()
        self.cache = json.loads(json_cache)

    def write_to_cache(self, data, filename):
        """ Writes data in JSON format to a file """

        json_data = self.json_format_dict(data, True)
        cache = open(filename, 'w')
        cache.write(json_data)
        cache.close()

    def to_safe(self, word):
        """ Converts 'bad' characters in a string to underscores so they can be used as Ansible groups """

        return re.sub("[^A-Za-z0-9\-]", "_", word)

    def json_format_dict(self, data, pretty=False):
        """ Converts a dict to a JSON object and dumps it as a formatted string """

        if pretty:
            return json.dumps(data, sort_keys=True, indent=2)
        else:
            return json.dumps(data)
Beispiel #22
0
def get_client(url, creds):
    [consumer_key, token, secret] = creds.split(':')
    auth = MAASOAuth(consumer_key=consumer_key,
                     resource_token=token,
                     resource_secret=secret)
    return MAASClient(auth, MAASDispatcher(), url)
Beispiel #23
0
 def _connect(self):
     if not self.conn:
         auth = MAASOAuth(*self.maas_api_key.split(':'))
         dispatcher = MAASDispatcher()
         self.maas_client = MAASClient(auth, dispatcher, self.maas_url)
         self.conn = True
def make_anonymous_api_client(server_url):
    """Create an unauthenticated API client."""
    return MAASClient(NoAuth(), MAASDispatcher(), server_url)
Beispiel #25
0
def _getclient(url=u'http://localhost/MAAS/api/1.0/'):
    consumer_key, token, secret = key('root').split(':', 3)
    auth = MAASOAuth(consumer_key, token, secret)
    dispatch = MAASDispatcher()
    client = MAASClient(auth, dispatch, url)
    return client