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()))] )
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()})
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 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')
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)
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()
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',
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()
""" 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 = []
# 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.