Example #1
0
 def test_get_state_by_name(self):
     """We expect the states created in setUp to be numbered sequentially
     """
     self.assertEqual(
         [ s.get_state_id_by_name(state) for state in s.get_state_list() ],
         [ n+1 for n in range(len(s.get_state_list()))]
     )
Example #2
0
    def test_states_empty(self):
        #Get a valid log-in
        app = TestApp(get_app(test_ini))
        settings = get_appsettings(test_ini)
        app.authorization = ('Basic', ('agent', settings['agent.secret']))

        r = app.get('/states')

        self.assertEqual(r.json, { s : 0 for s in server.get_state_list() })
    def test_states_empty(self):
        #Get a valid log-in
        app = TestApp(get_app(test_ini))
        settings = get_appsettings(test_ini)
        app.authorization = ('Basic', ('agent', settings['agent.secret']))

        r = app.get('/states')

        self.assertEqual(r.json, {s: 0 for s in server.get_state_list()})
Example #4
0
def retrieve_server_counts_by_state(request):
    """
    List all states and the number of servers in that state.
    """
    #Note that with the current DB schema, having the separate state and states calls is silly
    #because both retrieve the same info from the DB then selectively throw bits away.
    server_table = server.list_servers_by_state()

    all_states = server.get_state_list()

    #Not so good - we'd like to report all valid states...
    #return { k: len(v) for k, v in server_table }

    #Better...
    return {s: len(server_table.get(s, ())) for s in all_states}
Example #5
0
def retrieve_server_counts_by_state(request):
    """
    List all states and the number of servers in that state.
    """
    # Note that with the current DB schema, having the separate state and states calls is silly
    # because both retrieve the same info from the DB then selectively throw bits away.
    server_table = server.list_servers_by_state()

    all_states = server.get_state_list()

    # Not so good - we'd like to report all valid states...
    # return { k: len(v) for k, v in server_table }

    # Better...
    return {s: len(server_table.get(s, ())) for s in all_states}
    def test_conf_loaded(self):
        """There is a test.json.conf in the test directory, so just check
           that things from there got picked up by peering directly into
           the server object.
        """

        #test_the_test
        self.assertTrue(
            os.path.isfile(
                os.path.join(os.path.dirname(__file__), 'test.settings.json')))

        bl = server.get_boost_levels()
        self.assertEqual(bl['baseline']['label'], 'test_baseline')

        sl = server.get_state_list()
        self.assertEqual(sl[-1], 'TestExtraState')
    def test_new_conf(self):
        """This time, load an alternative file
        """
        newconf = os.path.join(os.path.dirname(__file__),
                               'test2.settings.json')

        #test_the_test
        self.assertTrue(os.path.isfile(newconf))

        server.load_config_json(newconf)

        bl = server.get_boost_levels()
        self.assertEqual(bl['baseline']['label'], 'test2_baseline')

        self.assertEqual(bl['levels'][0]['label'], 'test2_boost1')

        sl = server.get_state_list()
        self.assertEqual(sl[-1], 'Test2ExtraState')
Example #8
0
    def test_retrieve_state_summary(self):
        """ Test for /states
        """
        app = self.app
        # Generate base status table
        status_table = { s : 0 for s in server.get_state_list() }

        r = app.get("/states")
        self.assertEqual(r.json, status_table)

        for n in range(1, 6):
            self.create_server("testserver%i" % n)

        app.post('/servers/testserver1/Stopping')
        app.post('/servers/testserver2/Stopping')
        app.post('/servers/testserver3/Stopping')
        app.post('/servers/testserver4/Started')
        app.post('/servers/testserver5/Starting')

        # Test1 - servers set to only one state.
        st1 = status_table.copy()
        st1['Stopping'] = 3
        st1['Started']  = 1
        st1['Starting'] = 1

        r = app.get("/states")
        self.assertEqual(r.json, st1)

        # Test2 - server states have been changed
        app.post('/servers/testserver3/Started')
        app.post('/servers/testserver3/Stopping')
        app.post('/servers/testserver4/Stopping')
        app.post('/servers/testserver3/Starting')

        st2 = status_table.copy()
        st2['Stopping'] = 3
        st2['Started']  = 0
        st2['Starting'] = 2

        r = app.get("/states")
        self.assertEqual(r.json, st2)
Example #9
0
def main(global_config, **settings):
    """ Set routes, authentication policies, and add callbacks to modify
    responses."""

    agent_spec = [("agent", get_secret(settings, "agent"), "agents")]

    hap = HybridAuthenticationPolicy(hardcoded=agent_spec, secret=get_secret(settings, "authtkt"), realm="eos_db")

    config = Configurator(settings=settings, authentication_policy=hap, root_factory="eos_db.views.PermissionsMap")

    config.add_subscriber(add_cors_callback, NewRequest)
    config.add_subscriber(add_cookie_callback, NewRequest)

    # Needed to ensure proper 401 responses
    config.add_forbidden_view(hap.get_forbidden_view)

    # Do this if you need extra info generated by the Configurator, but
    # we do not.
    # settings = config.registry.settings

    # Set the engine, but only if it's not already set.  This is useful
    # for testing where we can re-initialise the webapp while leaving the
    # database in place.
    server.choose_engine(settings["server"], replace=False)

    # Top-level home page. Yields API call list.
    config.add_route("home", "/")

    # User-related API calls (callable by users)

    config.add_route("users", "/users")  # Return user list

    config.add_route("my_user", "/user")  # Return info about me (including credit)
    config.add_route("my_password", "/user/password")  # Set my password (only for admins or self)
    config.add_route("my_touches", "/user/touches")  # Get server touches

    # User-related API calls (callable by Actors/Admins)
    config.add_route("user", "/users/{name}")  # Get user details or
    # Put new user or
    # Delete user

    config.add_route("user_touches", "/users/{name}/touches")
    # Get server touches

    config.add_route("user_password", "/users/{name}/password")
    # Put new password
    # Get password verification by posting
    # password=asdf ??  Or not?

    config.add_route("user_credit", "/users/{name}/credit")
    # Put new credit or debit
    # Get current balance

    # Server-related API calls

    config.add_route("servers", "/servers")  # Return server list
    config.add_route("server", "/servers/{name}")  # Get server details or
    # Post new server or
    # Delete server

    # This is used by the agents.  Can help to be absolutely sure you are talking
    # about the right server.
    config.add_route("server_by_id", "/servers/by_id/{id}")

    # Server state-related calls.
    config.add_route("states", "/states")  # Get summary count of servers in each state
    config.add_route("state", "/states/{name}")  # Get list of servers in
    # the given state.
    config.add_route("deboosts", "/deboost_jobs")  # Get list of servers wanting deboost

    # Define PUT calls to put the server into various states.  Each call is backed
    # by a separate function in views.py, and mostly these just add a touch, but
    # they may implement custom functionality, for example to check and deduct
    # credit when boosting, or to limit who can change to certain states.
    for state in server.get_state_list():
        config.add_route("server_" + state, "/servers/{name}/" + state)
        config.add_route("server_by_id_" + state, "/servers/by_id/{id}/" + state)

    # Call to state, owner, touches and get/set specification.
    for action in ("specification", "state", "owner", "touches"):
        config.add_route("server_" + action, "/servers/{name}/" + action)
        config.add_route("server_by_id_" + action, "/servers/by_id/{id}/" + action)

    # Call the extend boost which does not correspond to any state change
    for action in ("extend_boost",):
        config.add_route("server_" + action, "/servers/{name}/" + action)
        config.add_route("server_by_id_" + action, "/servers/by_id/{id}/" + action)

    config.scan()
    return config.make_wsgi_app()
Example #10
0
    resp.headers['Allow'] = "HEAD,GET,OPTIONS"
    return resp


@view_config(request_method="OPTIONS",
             routes=['server', 'server_specification'])
@view_config(request_method="OPTIONS",
             routes=['server_by_id', 'server_by_id_specification'])
def options2(request):
    resp = Response(None)
    resp.headers['Allow'] = "HEAD,GET,POST,OPTIONS"
    return resp


@view_config(request_method="OPTIONS",
             routes=["server_" + x for x in server.get_state_list()])
@view_config(request_method="OPTIONS",
             routes=["server_by_id_" + x for x in server.get_state_list()])
@view_config(request_method="OPTIONS",
             routes=['server_extend_boost', 'server_by_id_extend_boost'])
def options3(request):
    resp = Response(None)
    resp.headers['Allow'] = "HEAD,POST,OPTIONS"
    return resp


# End of OPTIONS guff


@view_config(request_method="GET",
             route_name='users',
Example #11
0
def main(global_config, **settings):
    """ Set routes, authentication policies, and add callbacks to modify
    responses."""

    agent_spec = [('agent', get_secret(settings, 'agent'), 'agents')]

    hap = HybridAuthenticationPolicy(hardcoded=agent_spec,
                                     secret=get_secret(settings, "authtkt"),
                                     realm="eos_db")

    config = Configurator(settings=settings,
                          authentication_policy=hap,
                          root_factory='eos_db.views.PermissionsMap')

    config.add_subscriber(add_cors_callback, NewRequest)
    config.add_subscriber(add_cookie_callback, NewRequest)

    # Needed to ensure proper 401 responses
    config.add_forbidden_view(hap.get_forbidden_view)

    # Do this if you need extra info generated by the Configurator, but
    # we do not.
    #settings = config.registry.settings

    # Load the default configuration file, based on the .ini file name
    # And hey, I found a use for global_config!
    settings_json = global_config['__file__'][:-4] + ".settings.json"
    if os.path.isfile(settings_json):
        server.load_config_json(settings_json)

    # Set the engine, but only if it's not already set.  This is useful
    # for testing where we can re-initialise the webapp while leaving the
    # database in place.
    server.choose_engine(settings['server'], replace=False)

    # Endpoints that can be called without authentication
    # Top-level home page. Yields API call list.
    config.add_route('home', '/')
    # View of BoostLevels from settings.py
    config.add_route('boostlevels', '/boostlevels')

    # User-related API calls (callable by users)

    config.add_route('users', '/users')  # Return user list

    config.add_route('my_user',
                     '/user')  # Return info about me (including credit)
    config.add_route(
        'my_password',
        '/user/password')  # Set my password (only for admins or self)
    config.add_route('my_touches', '/user/touches')  # Get server touches

    # User-related API calls (callable by Actors/Admins)
    config.add_route('user', '/users/{name}')  # Get user details or
    # Put new user or
    # Delete user

    config.add_route('user_touches', '/users/{name}/touches')
    # Get server touches

    config.add_route('user_password', '/users/{name}/password')
    # Put new password
    # Get password verification by posting
    # password=asdf ??  Or not?

    config.add_route('user_credit', '/users/{name}/credit')
    # Put new credit or debit
    # Get current balance

    # Server-related API calls

    config.add_route('servers', '/servers')  # Return server list
    config.add_route('server', '/servers/{name}')  # Get server details or
    # Post new server or
    # Delete server

    #This is used by the agents.  Can help to be absolutely sure you are talking
    #about the right server.
    config.add_route('server_by_id', '/servers/by_id/{id}')

    # Server state-related calls.
    config.add_route('states',
                     '/states')  # Get summary count of servers in each state
    config.add_route('state', '/states/{name}')  # Get list of servers in
    # the given state.
    config.add_route('deboosts',
                     '/deboost_jobs')  # Get list of servers wanting deboost

    #Define PUT calls to put the server into various states.  Each call is backed
    #by a separate function in views.py, and mostly these just add a touch, but
    #they may implement custom functionality, for example to check and deduct
    #credit when boosting, or to limit who can change to certain states.
    for state in server.get_state_list():
        config.add_route('server_' + state, '/servers/{name}/' + state)
        config.add_route('server_by_id_' + state,
                         '/servers/by_id/{id}/' + state)

    #Call to state, owner, touches and get/set specification.
    for action in ('specification', 'state', 'owner', 'touches'):
        config.add_route('server_' + action, '/servers/{name}/' + action)
        config.add_route('server_by_id_' + action,
                         '/servers/by_id/{id}/' + action)

    # Call the extend boost which does not correspond to any state change
    for action in ('extend_boost', ):
        config.add_route('server_' + action, '/servers/{name}/' + action)
        config.add_route('server_by_id_' + action,
                         '/servers/by_id/{id}/' + action)

    config.scan()
    return config.make_wsgi_app()
Example #12
0
    """ Return the OPTIONS header. """
    # NOTE: This is important for enabling CORS, although under certain
    # circumstances the browser doesn' appear to need it. Might be worth
    # examining why.
    resp = Response(None)
    resp.headers['Allow'] = "HEAD,GET,OPTIONS"
    return resp

@view_config(request_method="OPTIONS", routes=['server', 'server_specification'])
@view_config(request_method="OPTIONS", routes=['server_by_id', 'server_by_id_specification'])
def options2(request):
    resp = Response(None)
    resp.headers['Allow'] = "HEAD,GET,POST,OPTIONS"
    return resp

@view_config(request_method="OPTIONS", routes=["server_" + x for x in server.get_state_list()])
@view_config(request_method="OPTIONS", routes=["server_by_id_" + x for x in server.get_state_list()])
@view_config(request_method="OPTIONS", routes=['server_extend_boost', 'server_by_id_extend_boost'])
def options3(request):
    resp = Response(None)
    resp.headers['Allow'] = "HEAD,POST,OPTIONS"
    return resp

# End of OPTIONS guff

@view_config(request_method="GET", route_name='users', renderer='json', permission="use")
def retrieve_users(request):
    """Return details for all users on the system.  Basically the same as calling /users/x
       for all users, but missing the credit info.
    """
    res = []
Example #13
0
    # circumstances the browser doesn' appear to need it. Might be worth
    # examining why.
    resp = Response(None)
    resp.headers["Allow"] = "HEAD,GET,OPTIONS"
    return resp


@view_config(request_method="OPTIONS", routes=["server", "server_specification"])
@view_config(request_method="OPTIONS", routes=["server_by_id", "server_by_id_specification"])
def options2(request):
    resp = Response(None)
    resp.headers["Allow"] = "HEAD,GET,POST,OPTIONS"
    return resp


@view_config(request_method="OPTIONS", routes=["server_" + x for x in server.get_state_list()])
@view_config(request_method="OPTIONS", routes=["server_by_id_" + x for x in server.get_state_list()])
@view_config(request_method="OPTIONS", routes=["server_extend_boost", "server_by_id_extend_boost"])
def options3(request):
    resp = Response(None)
    resp.headers["Allow"] = "HEAD,POST,OPTIONS"
    return resp


# End of OPTIONS guff


@view_config(request_method="GET", route_name="users", renderer="json", permission="use")
def retrieve_users(request):
    """Return details for all users on the system.  Basically the same as calling /users/x
       for all users, but missing the credit info.