Ejemplo n.º 1
0
    def on_put(self, req, resp, name):
        """
        Handles the creation of a new Cluster.

        :param req: Request instance that will be passed through.
        :type req: falcon.Request
        :param resp: Response instance that will be passed through.
        :type resp: falcon.Response
        :param name: The name of the Cluster being created.
        :type name: str
        """
        # PUT is idempotent, and since there's no body to this request,
        # there's nothing to conflict with.  The request should always
        # succeed, even if we didn't actually do anything.
        try:
            store_manager = cherrypy.engine.publish('get-store-manager')[0]
            cluster = store_manager.get(Cluster.new(name=name))
            self.logger.info(
                'Creation of already exisiting cluster {0} requested.'.format(
                    name))
        except:
            pass

        args = {}
        data = req.stream.read()
        if data:
            try:
                args = json.loads(data.decode())
                self.logger.debug('Cluster args received: "{0}"'.format(args))
            except ValueError as error:
                self.logger.error(
                    'Unable to parse cluster arguments: {0}'.format(error))
        try:
            self.logger.debug('Looking for network {0}'.format(
                args['network']))
            network = store_manager.get(
                Network.new(name=args['network']))
        except KeyError:
            network = Network.new(**C.DEFAULT_CLUSTER_NETWORK_JSON)
        cluster = Cluster.new(
            name=name, type=args.get('type', C.CLUSTER_TYPE_DEFAULT),
            network=network.name, status='ok', hostset=[])
        self.logger.debug('Cluster to create: {0}'.format(
            cluster.to_json_with_hosts()))
        store_manager.save(cluster)
        self.logger.info(
            'Created cluster {0} per request.'.format(name))
        self.logger.debug('New Cluster: {0}'.format(
            cluster.to_json_with_hosts()))
        resp.status = falcon.HTTP_201
Ejemplo n.º 2
0
    def on_put(self, req, resp, name):
        """
        Handles the creation of a new Network.

        :param req: Request instance that will be passed through.
        :type req: falcon.Request
        :param resp: Response instance that will be passed through.
        :type resp: falcon.Response
        :param name: The friendly name of the network.
        :type address: str
        """
        try:
            req_data = req.stream.read()
            req_body = json.loads(req_data.decode())
            network_type = req_body['type']
            options = req_body.get('options', {})
        except (KeyError, ValueError):
            self.logger.info(
                'Bad client PUT request for network {0}: {1}'.format(
                    name, req_data))
            resp.status = falcon.HTTP_400
            return

        store_manager = cherrypy.engine.publish('get-store-manager')[0]
        # If the type is flannel_etcd yet we have not etcd backend configured
        # don't create and notify the caller
        if network_type == C.NETWORK_TYPE_FLANNEL_ETCD:
            backend_found = False
            for handler_type, _, _ in store_manager.list_store_handlers():
                if handler_type is EtcdStoreHandler:
                    backend_found = True
                    break

            if not backend_found:
                self.logger.info(
                    'Network {0} can not be created as type flannel_etcd '
                    'as no etcd backend is configured.'.format(name))
                resp.status = falcon.HTTP_CONFLICT
                return

        network = Network.new(name=name, type=network_type, options=options)
        self.logger.debug('Saving network: {0}'.format(network.to_json()))
        store_manager.save(network)

        resp.status = falcon.HTTP_CREATED
        req.context['model'] = network
Ejemplo n.º 3
0
    def on_get(self, req, resp, name):
        """
        Handles retrieval of an existing Network.

        :param req: Request instance that will be passed through.
        :type req: falcon.Request
        :param resp: Response instance that will be passed through.
        :type resp: falcon.Response
        :param name: The friendly name of the network.
        :type address: str
        """
        try:
            store_manager = cherrypy.engine.publish('get-store-manager')[0]
            network = store_manager.get(Network.new(name=name))
            resp.status = falcon.HTTP_200
            req.context['model'] = network
        except:
            resp.status = falcon.HTTP_404
            return
Ejemplo n.º 4
0
    def on_delete(self, req, resp, name):
        """
        Handles the Deletion of a Network.

        :param req: Request instance that will be passed through.
        :type req: falcon.Request
        :param resp: Response instance that will be passed through.
        :type resp: falcon.Response
        :param name: The friendly name of the network.
        :type address: str
        """
        resp.body = '{}'
        store_manager = cherrypy.engine.publish('get-store-manager')[0]
        try:
            store_manager.delete(Network.new(name=name))
            resp.status = falcon.HTTP_200
        except Exception as error:
            self.logger.warn('{}: {}'.format(type(error), error))
            resp.status = falcon.HTTP_404
Ejemplo n.º 5
0
    def test_bootstrap(self):
        """
        Verify Transport().bootstrap works as expected.
        """
        with patch(
                'commissaire.transport.ansibleapi.TaskQueueManager') as _tqm:
            _tqm().run.return_value = 0

            transport = ansibleapi.Transport()
            transport.variable_manager._fact_cache = {}
            oscmd = MagicMock(OSCmdBase)

            result, facts = transport.bootstrap('10.2.0.2',
                                                Cluster.new().__dict__,
                                                'test/fake_key', MagicMock(),
                                                oscmd)
            # We should have a successful response
            self.assertEquals(0, result)
            # We should see expected calls
            self.assertEquals(1, oscmd.install_docker.call_count)
            self.assertEquals(1, oscmd.install_kube.call_count)

            # Check user-config to playbook-variable translation.
            etcd_config = {
                'server_url': 'https://192.168.1.1:1234',
                'certificate_ca_path': '/path/to/etcd/ca/cert',
                'certificate_path': '/path/to/etcd/client/cert',
                'certificate_key_path': '/path/to/etcd/client/key'
            }
            kube_config = {
                'server_url': 'https://192.168.2.2:4567',
                'certificate_path': '/path/to/kube/client/cert',
                'certificate_key_path': '/path/to/kube/client/key'
            }
            store_manager = MagicMock(StoreHandlerManager)
            store_manager.list_store_handlers.return_value = [
                (EtcdStoreHandler, etcd_config, ()),
                (KubernetesStoreHandler, kube_config, ())
            ]

            store_manager.get.return_value = Network.new(name='default',
                                                         type='flannel_etcd')

            cluster_data = Cluster.new(name='default',
                                       network='default').__dict__

            transport = ansibleapi.Transport()
            transport._run = MagicMock()
            transport._run.return_value = (0, {})
            result, facts = transport.bootstrap('10.2.0.2', cluster_data,
                                                'test/fake_key', store_manager,
                                                oscmd)
            play_vars = transport._run.call_args[0][4]
            self.assertEqual(play_vars['commissaire_etcd_server_url'],
                             'https://192.168.1.1:1234')
            self.assertEqual(play_vars['commissaire_etcd_ca_path_local'],
                             '/path/to/etcd/ca/cert')
            self.assertEqual(
                play_vars['commissaire_etcd_client_cert_path_local'],
                '/path/to/etcd/client/cert')
            self.assertEqual(
                play_vars['commissaire_etcd_client_key_path_local'],
                '/path/to/etcd/client/key')

            # Check 'commissaire_enable_pkg_repos' playbook variable
            # for various operating systems.
            transport = ansibleapi.Transport()
            transport._run = MagicMock()
            transport._run.return_value = (0, {})

            needs_enable_repos = ('redhat', 'rhel')

            for os_type in available_os_types:
                oscmd = get_oscmd(os_type)
                result, facts = transport.bootstrap('10.2.0.2.', cluster_data,
                                                    'test/fake_key',
                                                    MagicMock(), oscmd)
                play_vars = transport._run.call_args[0][4]
                command = play_vars['commissaire_enable_pkg_repos']
                if os_type in needs_enable_repos:
                    self.assertIn('subscription-manager repos', command)
                else:
                    self.assertEqual('true', command)  # no-op command
Ejemplo n.º 6
0
    def bootstrap(self, ip, cluster_data, key_file, store_manager, oscmd):
        """
        Bootstraps a host via ansible.

        :param ip: IP address to bootstrap.
        :type ip: str
        :param cluster_data: The data required to create a Cluster instance.
        :type cluster_data: dict or None
        :param key_file: Full path to the file holding the private SSH key.
        :type key_file: str
        :param store_manager: Remote object for remote stores
        :type store_manager: commissaire.store.storehandlermanager.
                             StoreHandlerManager
        :param oscmd: OSCmd class to use
        :type oscmd: commissaire.oscmd.OSCmdBase
        :returns: tuple -- (exitcode(int), facts(dict)).
        """
        self.logger.debug('Using {0} as the oscmd class for {1}'.format(
            oscmd.os_type, ip))

        # cluster_data can be None. If it is change it to an empty dict
        if cluster_data is None:
            cluster_data = {}
        cluster_type = C.CLUSTER_TYPE_HOST
        network = Network.new(**C.DEFAULT_CLUSTER_NETWORK_JSON)
        try:
            self.logger.debug(
                'Grabbing cluster type from {0}'.format(cluster_data))
            cluster = Cluster.new(**cluster_data)
            cluster_type = cluster.type
            self.logger.debug('Found network {0}'.format(cluster.network))
            network = store_manager.get(Network.new(name=cluster.network))
        except KeyError:
            # Not part of a cluster
            pass

        etcd_config = self._get_etcd_config(store_manager)
        kube_config = self._get_kube_config(store_manager)

        play_vars = {
            'commissaire_cluster_type':
            cluster_type,
            'commissaire_bootstrap_ip':
            ip,
            'commissaire_kubernetes_api_server_url':
            kube_config['server_url'],
            'commissaire_kubernetes_bearer_token':
            kube_config['token'],
            # TODO: Where do we get this?
            'commissaire_docker_registry_host':
            '127.0.0.1',
            # TODO: Where do we get this?
            'commissaire_docker_registry_port':
            8080,
            # TODO: Where do we get this?
            'commissaire_flannel_key':
            '/atomic01/network',
            'commissaire_docker_config_local':
            resource_filename('commissaire', 'data/templates/docker'),
            'commissaire_flanneld_config_local':
            resource_filename('commissaire', 'data/templates/flanneld'),
            'commissaire_kubelet_config_local':
            resource_filename('commissaire', 'data/templates/kubelet'),
            'commissaire_kubernetes_config_local':
            resource_filename('commissaire', 'data/templates/kube_config'),
            'commissaire_kubeconfig_config_local':
            resource_filename('commissaire', 'data/templates/kubeconfig'),
            'commissaire_install_libselinux_python':
            " ".join(oscmd.install_libselinux_python()),
            'commissaire_docker_config':
            oscmd.docker_config,
            'commissaire_flanneld_config':
            oscmd.flanneld_config,
            'commissaire_kubelet_config':
            oscmd.kubelet_config,
            'commissaire_kubernetes_config':
            oscmd.kubernetes_config,
            'commissaire_kubeconfig_config':
            oscmd.kubernetes_kubeconfig,
            'commissaire_install_flannel':
            " ".join(oscmd.install_flannel()),
            'commissaire_install_docker':
            " ".join(oscmd.install_docker()),
            'commissaire_install_kube':
            " ".join(oscmd.install_kube()),
            'commissaire_flannel_service':
            oscmd.flannel_service,
            'commissaire_docker_service':
            oscmd.flannel_service,
            'commissaire_kubelet_service':
            oscmd.kubelet_service,
            'commissaire_kubeproxy_service':
            oscmd.kubelet_proxy_service,
        }

        # If we are a flannel_server network then set the var
        if network.type == 'flannel_server':
            play_vars['commissaire_flanneld_server'] = network.options.get(
                'address')
        elif network.type == 'flannel_etcd':
            play_vars['commissaire_etcd_server_url'] = etcd_config[
                'server_url']

        # Provide the CA if etcd is being used over https
        if (etcd_config['server_url'].startswith('https:')
                and 'certificate_ca_path' in etcd_config):
            play_vars['commissaire_etcd_ca_path'] = oscmd.etcd_ca
            play_vars['commissaire_etcd_ca_path_local'] = (
                etcd_config['certificate_ca_path'])

        # Client Certificate additions
        if 'certificate_path' in etcd_config:
            self.logger.info('Using etcd client certs')
            play_vars['commissaire_etcd_client_cert_path'] = (
                oscmd.etcd_client_cert)
            play_vars['commissaire_etcd_client_cert_path_local'] = (
                etcd_config['certificate_path'])
            play_vars['commissaire_etcd_client_key_path'] = (
                oscmd.etcd_client_key)
            play_vars['commissaire_etcd_client_key_path_local'] = (
                etcd_config['certificate_key_path'])

        if 'certificate_path' in kube_config:
            self.logger.info('Using kubernetes client certs')
            play_vars['commissaire_kubernetes_client_cert_path'] = (
                oscmd.kube_client_cert)
            play_vars['commissaire_kubernetes_client_cert_path_local'] = (
                kube_config['certificate_path'])
            play_vars['commissaire_kubernetes_client_key_path'] = (
                oscmd.kube_client_key)
            play_vars['commissaire_kubernetes_client_key_path_local'] = (
                kube_config['certificate_key_path'])

        # XXX: Need to enable some package repositories for OS 'rhel'
        #      (or 'redhat').  This is a hack for a single corner case.
        #      We discussed how to generalize future cases where we need
        #      extra commands for a specific OS but decided to defer until
        #      more crop up.
        #
        #      See https://github.com/projectatomic/commissaire/pull/56
        #
        if oscmd.os_type in ('rhel', 'redhat'):
            play_vars['commissaire_enable_pkg_repos'] = (
                'subscription-manager repos '
                '--enable=rhel-7-server-extras-rpms '
                '--enable=rhel-7-server-optional-rpms')
        else:
            play_vars['commissaire_enable_pkg_repos'] = 'true'

        self.logger.debug('Variables for bootstrap: {0}'.format(play_vars))

        play_file = resource_filename('commissaire',
                                      'data/ansible/playbooks/bootstrap.yaml')
        results = self._run(ip, key_file, play_file, [0], play_vars)

        return results