notification changes
Mostly done, but does need proper tests. * shift actions into their own sub folder structure for clarity * create new sub folder for notications * update other code to reflect those changes * add first basic notification engine * add RT notification engine * minor django url and context changes to avoid future deprecation * getting rid of secondary migration for column rename (as not in prod) Change-Id: I46932b6d78b93e86580506c887548fd24c0750f5
This commit is contained in:
parent
d4759d6734
commit
95e9eb4ba3
@ -1,3 +1,5 @@
|
||||
include README.md
|
||||
|
||||
graft stacktask/api/v*/templates
|
||||
graft stacktask/notifications/templates
|
||||
graft stacktask/notifications/*/templates
|
@ -8,7 +8,8 @@ ALLOWED_HOSTS:
|
||||
|
||||
ADDITIONAL_APPS:
|
||||
- stacktask.api.v1
|
||||
- stacktask.tenant_setup
|
||||
- stacktask.actions.tenant_setup
|
||||
- stacktask.notifications.request_tracker
|
||||
|
||||
DATABASES:
|
||||
default:
|
||||
@ -80,6 +81,19 @@ TASK_SETTINGS:
|
||||
reply: no-reply@example.com
|
||||
template: completed.txt
|
||||
html_template: completed.txt
|
||||
notifications:
|
||||
EmailNotification:
|
||||
emails:
|
||||
- example@example.com
|
||||
reply: no-reply@example.com
|
||||
template: notification.txt
|
||||
html_template: completed.txt
|
||||
RTNotification:
|
||||
url: http://localhost/rt/REST/1.0/
|
||||
queue: helpdesk
|
||||
username: example@example.com
|
||||
password: password
|
||||
template: notification.txt
|
||||
invite_user:
|
||||
emails:
|
||||
# To not send this email, set the value to null,
|
||||
|
@ -6,3 +6,5 @@ python-keystoneclient>=1.0.0
|
||||
python-neutronclient>=2.3.10
|
||||
jsonfield>=1.0.2
|
||||
django-rest-swagger>=0.3.3
|
||||
pyyaml>=3.11
|
||||
rt>=1.0.8
|
||||
|
11
setup.py
11
setup.py
@ -4,7 +4,7 @@ from setuptools import setup, find_packages
|
||||
setup(
|
||||
name='stacktask',
|
||||
|
||||
version='0.1.1a1',
|
||||
version='0.1.1a2',
|
||||
description='A user task service for openstack.',
|
||||
long_description=(
|
||||
'A task service to sit alongside keystone and ' +
|
||||
@ -23,7 +23,11 @@ setup(
|
||||
keywords='openstack keystone users tasks registration workflow',
|
||||
|
||||
packages=find_packages(),
|
||||
package_data={'stacktask': ['api/v*/templates/*.txt']},
|
||||
package_data={
|
||||
'stacktask': [
|
||||
'api/v*/templates/*.txt',
|
||||
'notifications/templates/*.txt',
|
||||
'notifications/*/templates/*.txt']},
|
||||
|
||||
install_requires=[
|
||||
'Django>=1.7.3',
|
||||
@ -34,7 +38,8 @@ setup(
|
||||
'python-keystoneclient>=1.0.0',
|
||||
'python-neutronclient>=2.3.10',
|
||||
'pyyaml>=3.11',
|
||||
'django-rest-swagger>=0.3.3'
|
||||
'django-rest-swagger>=0.3.3',
|
||||
'rt>=1.0.8',
|
||||
],
|
||||
entry_points={
|
||||
'console_scripts': [
|
||||
|
@ -14,8 +14,8 @@
|
||||
|
||||
from django.db import models
|
||||
from django.utils import timezone
|
||||
from stacktask.base import user_store
|
||||
from stacktask.base import serializers
|
||||
from stacktask.actions import user_store
|
||||
from stacktask.actions import serializers
|
||||
from django.conf import settings
|
||||
from jsonfield import JSONField
|
||||
from logging import getLogger
|
||||
@ -389,7 +389,11 @@ class NewProject(UserNameAction):
|
||||
'email'
|
||||
]
|
||||
|
||||
default_roles = {"Member", "project_owner", "project_mod", "_member_", "heat_stack_owner"}
|
||||
# NOTE(adriant): move these to a config somewhere?
|
||||
default_roles = {
|
||||
"Member", "project_owner", "project_mod", "_member_",
|
||||
"heat_stack_owner"
|
||||
}
|
||||
|
||||
def _validate(self):
|
||||
project_valid = self._validate_project()
|
@ -1,4 +1,4 @@
|
||||
from base.models import BaseAction
|
||||
from actions.models import BaseAction
|
||||
from serializers import NewClientSerializer
|
||||
from django.conf import settings
|
||||
|
@ -12,11 +12,11 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from stacktask.base.models import BaseAction
|
||||
from stacktask.tenant_setup.serializers import DefaultProjectResourcesSerializer
|
||||
from stacktask.actions.models import BaseAction
|
||||
from stacktask.actions.tenant_setup.serializers import DefaultProjectResourcesSerializer
|
||||
from django.conf import settings
|
||||
from stacktask.base.user_store import IdentityManager
|
||||
from stacktask.base import openstack_clients
|
||||
from stacktask.actions.user_store import IdentityManager
|
||||
from stacktask.actions import openstack_clients
|
||||
|
||||
|
||||
class DefaultProjectResources(BaseAction):
|
@ -16,7 +16,8 @@ from django.test import TestCase
|
||||
from stacktask.api.models import Task
|
||||
from stacktask.api.v1.tests import FakeManager, setup_temp_cache
|
||||
from stacktask.api.v1 import tests
|
||||
from stacktask.tenant_setup.models import DefaultProjectResources, AddAdminToProject
|
||||
from stacktask.actions.tenant_setup.models import (
|
||||
DefaultProjectResources, AddAdminToProject)
|
||||
import mock
|
||||
|
||||
|
||||
@ -72,8 +73,9 @@ def get_fake_neutron():
|
||||
|
||||
class TenantSetupActionTests(TestCase):
|
||||
|
||||
@mock.patch('stacktask.tenant_setup.models.IdentityManager', FakeManager)
|
||||
@mock.patch('stacktask.tenant_setup.models.openstack_clients.get_neutronclient',
|
||||
@mock.patch('stacktask.actions.tenant_setup.models.IdentityManager',
|
||||
FakeManager)
|
||||
@mock.patch('stacktask.actions.tenant_setup.models.openstack_clients.get_neutronclient',
|
||||
get_fake_neutron)
|
||||
def test_resource_setup(self):
|
||||
"""
|
||||
@ -110,8 +112,9 @@ class TenantSetupActionTests(TestCase):
|
||||
self.assertEquals(len(neutron_cache['routers']), 1)
|
||||
self.assertEquals(len(neutron_cache['subnets']), 1)
|
||||
|
||||
@mock.patch('stacktask.tenant_setup.models.IdentityManager', FakeManager)
|
||||
@mock.patch('stacktask.tenant_setup.models.openstack_clients.get_neutronclient',
|
||||
@mock.patch('stacktask.actions.tenant_setup.models.IdentityManager',
|
||||
FakeManager)
|
||||
@mock.patch('stacktask.actions.tenant_setup.models.openstack_clients.get_neutronclient',
|
||||
get_fake_neutron)
|
||||
def test_resource_setup_no_id(self):
|
||||
"""
|
||||
@ -141,8 +144,9 @@ class TenantSetupActionTests(TestCase):
|
||||
self.assertEquals(len(neutron_cache['routers']), 0)
|
||||
self.assertEquals(len(neutron_cache['subnets']), 0)
|
||||
|
||||
@mock.patch('stacktask.tenant_setup.models.IdentityManager', FakeManager)
|
||||
@mock.patch('stacktask.tenant_setup.models.openstack_clients.get_neutronclient',
|
||||
@mock.patch('stacktask.actions.tenant_setup.models.IdentityManager',
|
||||
FakeManager)
|
||||
@mock.patch('stacktask.actions.tenant_setup.models.openstack_clients.get_neutronclient',
|
||||
get_fake_neutron)
|
||||
def test_resource_setup_no_setup(self):
|
||||
"""
|
||||
@ -174,8 +178,9 @@ class TenantSetupActionTests(TestCase):
|
||||
self.assertEquals(len(neutron_cache['routers']), 0)
|
||||
self.assertEquals(len(neutron_cache['subnets']), 0)
|
||||
|
||||
@mock.patch('stacktask.tenant_setup.models.IdentityManager', FakeManager)
|
||||
@mock.patch('stacktask.tenant_setup.models.openstack_clients.get_neutronclient',
|
||||
@mock.patch('stacktask.actions.tenant_setup.models.IdentityManager',
|
||||
FakeManager)
|
||||
@mock.patch('stacktask.actions.tenant_setup.models.openstack_clients.get_neutronclient',
|
||||
get_fake_neutron)
|
||||
def test_resource_setup_fail(self):
|
||||
"""
|
||||
@ -231,7 +236,8 @@ class TenantSetupActionTests(TestCase):
|
||||
self.assertEquals(len(neutron_cache['routers']), 1)
|
||||
self.assertEquals(len(neutron_cache['subnets']), 1)
|
||||
|
||||
@mock.patch('stacktask.tenant_setup.models.IdentityManager', FakeManager)
|
||||
@mock.patch('stacktask.actions.tenant_setup.models.IdentityManager',
|
||||
FakeManager)
|
||||
def test_add_admin(self):
|
||||
"""
|
||||
Base case, adds admin user with admin role to project.
|
||||
@ -259,7 +265,8 @@ class TenantSetupActionTests(TestCase):
|
||||
project = tests.temp_cache['projects']['test_project']
|
||||
self.assertEquals(project.roles['admin'], ['admin'])
|
||||
|
||||
@mock.patch('stacktask.tenant_setup.models.IdentityManager', FakeManager)
|
||||
@mock.patch('stacktask.actions.tenant_setup.models.IdentityManager',
|
||||
FakeManager)
|
||||
def test_add_admin_reapprove(self):
|
||||
"""
|
||||
Ensure nothing happens or changes if rerun of approve.
|
@ -16,16 +16,18 @@ from django.test import TestCase
|
||||
from stacktask.api.models import Task
|
||||
from stacktask.api.v1.tests import FakeManager, setup_temp_cache
|
||||
from stacktask.api.v1 import tests
|
||||
from stacktask.base.models import NewUser, NewProject, ResetUser, EditUserRoles
|
||||
from stacktask.actions.models import (
|
||||
NewUser, NewProject, ResetUser, EditUserRoles)
|
||||
import mock
|
||||
|
||||
|
||||
class BaseActionTests(TestCase):
|
||||
class ActionTests(TestCase):
|
||||
|
||||
@mock.patch('stacktask.base.models.user_store.IdentityManager', FakeManager)
|
||||
@mock.patch('stacktask.actions.models.user_store.IdentityManager',
|
||||
FakeManager)
|
||||
def test_new_user(self):
|
||||
"""
|
||||
Test the base case, all valid.
|
||||
Test the default case, all valid.
|
||||
No existing user, valid tenant.
|
||||
"""
|
||||
project = mock.Mock()
|
||||
@ -67,7 +69,8 @@ class BaseActionTests(TestCase):
|
||||
|
||||
self.assertEquals(project.roles['test@example.com'], ['Member'])
|
||||
|
||||
@mock.patch('stacktask.base.models.user_store.IdentityManager', FakeManager)
|
||||
@mock.patch('stacktask.actions.models.user_store.IdentityManager',
|
||||
FakeManager)
|
||||
def test_new_user_existing(self):
|
||||
"""
|
||||
Existing user, valid tenant, no role.
|
||||
@ -85,9 +88,9 @@ class BaseActionTests(TestCase):
|
||||
setup_temp_cache({'test_project': project}, {user.name: user})
|
||||
|
||||
task = Task.objects.create(
|
||||
ip_address="0.0.0.0", keystone_user={'roles': ['admin',
|
||||
'project_mod'],
|
||||
'project_id': 'test_project_id'})
|
||||
ip_address="0.0.0.0", keystone_user={
|
||||
'roles': ['admin', 'project_mod'],
|
||||
'project_id': 'test_project_id'})
|
||||
|
||||
data = {
|
||||
'email': 'test@example.com',
|
||||
@ -109,7 +112,8 @@ class BaseActionTests(TestCase):
|
||||
|
||||
self.assertEquals(project.roles[user.name], ['Member'])
|
||||
|
||||
@mock.patch('stacktask.base.models.user_store.IdentityManager', FakeManager)
|
||||
@mock.patch('stacktask.actions.models.user_store.IdentityManager',
|
||||
FakeManager)
|
||||
def test_new_user_existing_role(self):
|
||||
"""
|
||||
Existing user, valid tenant, has role.
|
||||
@ -131,9 +135,9 @@ class BaseActionTests(TestCase):
|
||||
setup_temp_cache({'test_project': project}, {user.name: user})
|
||||
|
||||
task = Task.objects.create(
|
||||
ip_address="0.0.0.0", keystone_user={'roles': ['admin',
|
||||
'project_mod'],
|
||||
'project_id': 'test_project_id'})
|
||||
ip_address="0.0.0.0", keystone_user={
|
||||
'roles': ['admin', 'project_mod'],
|
||||
'project_id': 'test_project_id'})
|
||||
|
||||
data = {
|
||||
'email': 'test@example.com',
|
||||
@ -156,7 +160,8 @@ class BaseActionTests(TestCase):
|
||||
|
||||
self.assertEquals(project.roles[user.name], ['Member'])
|
||||
|
||||
@mock.patch('stacktask.base.models.user_store.IdentityManager', FakeManager)
|
||||
@mock.patch('stacktask.actions.models.user_store.IdentityManager',
|
||||
FakeManager)
|
||||
def test_new_user_no_tenant(self):
|
||||
"""
|
||||
No user, no tenant.
|
||||
@ -165,9 +170,9 @@ class BaseActionTests(TestCase):
|
||||
setup_temp_cache({}, {})
|
||||
|
||||
task = Task.objects.create(
|
||||
ip_address="0.0.0.0", keystone_user={'roles': ['admin',
|
||||
'project_mod'],
|
||||
'project_id': 'test_project_id'})
|
||||
ip_address="0.0.0.0", keystone_user={
|
||||
'roles': ['admin', 'project_mod'],
|
||||
'project_id': 'test_project_id'})
|
||||
|
||||
data = {
|
||||
'email': 'test@example.com',
|
||||
@ -189,7 +194,8 @@ class BaseActionTests(TestCase):
|
||||
|
||||
self.assertEquals('admin' in tests.temp_cache['users'], True)
|
||||
|
||||
@mock.patch('stacktask.base.models.user_store.IdentityManager', FakeManager)
|
||||
@mock.patch('stacktask.actions.models.user_store.IdentityManager',
|
||||
FakeManager)
|
||||
def test_new_project(self):
|
||||
"""
|
||||
Base case, no project, no user.
|
||||
@ -201,9 +207,9 @@ class BaseActionTests(TestCase):
|
||||
setup_temp_cache({}, {})
|
||||
|
||||
task = Task.objects.create(
|
||||
ip_address="0.0.0.0", keystone_user={'roles': ['admin',
|
||||
'project_mod'],
|
||||
'project_id': 'test_project_id'})
|
||||
ip_address="0.0.0.0", keystone_user={
|
||||
'roles': ['admin', 'project_mod'],
|
||||
'project_id': 'test_project_id'})
|
||||
|
||||
data = {
|
||||
'email': 'test@example.com',
|
||||
@ -233,9 +239,11 @@ class BaseActionTests(TestCase):
|
||||
project = tests.temp_cache['projects']['test_project']
|
||||
self.assertEquals(
|
||||
sorted(project.roles['test@example.com']),
|
||||
sorted(['Member', '_member_', 'project_owner', 'project_mod', 'heat_stack_owner']))
|
||||
sorted(['Member', '_member_', 'project_owner',
|
||||
'project_mod', 'heat_stack_owner']))
|
||||
|
||||
@mock.patch('stacktask.base.models.user_store.IdentityManager', FakeManager)
|
||||
@mock.patch('stacktask.actions.models.user_store.IdentityManager',
|
||||
FakeManager)
|
||||
def test_new_project_reapprove(self):
|
||||
"""
|
||||
Project created at post_approve step,
|
||||
@ -245,9 +253,9 @@ class BaseActionTests(TestCase):
|
||||
setup_temp_cache({}, {})
|
||||
|
||||
task = Task.objects.create(
|
||||
ip_address="0.0.0.0", keystone_user={'roles': ['admin',
|
||||
'project_mod'],
|
||||
'project_id': 'test_project_id'})
|
||||
ip_address="0.0.0.0", keystone_user={
|
||||
'roles': ['admin', 'project_mod'],
|
||||
'project_id': 'test_project_id'})
|
||||
|
||||
data = {
|
||||
'email': 'test@example.com',
|
||||
@ -285,9 +293,11 @@ class BaseActionTests(TestCase):
|
||||
project = tests.temp_cache['projects']['test_project']
|
||||
self.assertEquals(
|
||||
sorted(project.roles['test@example.com']),
|
||||
sorted(['Member', '_member_', 'project_owner', 'project_mod', 'heat_stack_owner']))
|
||||
sorted(['Member', '_member_', 'project_owner',
|
||||
'project_mod', 'heat_stack_owner']))
|
||||
|
||||
@mock.patch('stacktask.base.models.user_store.IdentityManager', FakeManager)
|
||||
@mock.patch('stacktask.actions.models.user_store.IdentityManager',
|
||||
FakeManager)
|
||||
def test_new_project_existing_user(self):
|
||||
"""
|
||||
no project, existing user.
|
||||
@ -301,9 +311,9 @@ class BaseActionTests(TestCase):
|
||||
setup_temp_cache({}, {user.name: user})
|
||||
|
||||
task = Task.objects.create(
|
||||
ip_address="0.0.0.0", keystone_user={'roles': ['admin',
|
||||
'project_mod'],
|
||||
'project_id': 'test_project_id'})
|
||||
ip_address="0.0.0.0", keystone_user={
|
||||
'roles': ['admin', 'project_mod'],
|
||||
'project_id': 'test_project_id'})
|
||||
|
||||
data = {
|
||||
'email': 'test@example.com',
|
||||
@ -332,9 +342,11 @@ class BaseActionTests(TestCase):
|
||||
project = tests.temp_cache['projects']['test_project']
|
||||
self.assertEquals(
|
||||
sorted(project.roles['test@example.com']),
|
||||
sorted(['Member', '_member_', 'project_owner', 'project_mod', 'heat_stack_owner']))
|
||||
sorted(['Member', '_member_', 'project_owner',
|
||||
'project_mod', 'heat_stack_owner']))
|
||||
|
||||
@mock.patch('stacktask.base.models.user_store.IdentityManager', FakeManager)
|
||||
@mock.patch('stacktask.actions.models.user_store.IdentityManager',
|
||||
FakeManager)
|
||||
def test_new_project_existing(self):
|
||||
"""
|
||||
Existing project.
|
||||
@ -348,9 +360,9 @@ class BaseActionTests(TestCase):
|
||||
setup_temp_cache({project.name: project}, {})
|
||||
|
||||
task = Task.objects.create(
|
||||
ip_address="0.0.0.0", keystone_user={'roles': ['admin',
|
||||
'project_mod'],
|
||||
'project_id': 'test_project_id'})
|
||||
ip_address="0.0.0.0", keystone_user={
|
||||
'roles': ['admin', 'project_mod'],
|
||||
'project_id': 'test_project_id'})
|
||||
|
||||
data = {
|
||||
'email': 'test@example.com',
|
||||
@ -365,7 +377,8 @@ class BaseActionTests(TestCase):
|
||||
action.post_approve()
|
||||
self.assertEquals(action.valid, False)
|
||||
|
||||
@mock.patch('stacktask.base.models.user_store.IdentityManager', FakeManager)
|
||||
@mock.patch('stacktask.actions.models.user_store.IdentityManager',
|
||||
FakeManager)
|
||||
def test_reset_user(self):
|
||||
"""
|
||||
Base case, existing user.
|
||||
@ -380,9 +393,9 @@ class BaseActionTests(TestCase):
|
||||
setup_temp_cache({}, {user.name: user})
|
||||
|
||||
task = Task.objects.create(
|
||||
ip_address="0.0.0.0", keystone_user={'roles': ['admin',
|
||||
'project_mod'],
|
||||
'project_id': 'test_project_id'})
|
||||
ip_address="0.0.0.0", keystone_user={
|
||||
'roles': ['admin', 'project_mod'],
|
||||
'project_id': 'test_project_id'})
|
||||
|
||||
data = {
|
||||
'email': 'test@example.com',
|
||||
@ -405,7 +418,8 @@ class BaseActionTests(TestCase):
|
||||
tests.temp_cache['users']['test@example.com'].password,
|
||||
'123456')
|
||||
|
||||
@mock.patch('stacktask.base.models.user_store.IdentityManager', FakeManager)
|
||||
@mock.patch('stacktask.actions.models.user_store.IdentityManager',
|
||||
FakeManager)
|
||||
def test_reset_user_no_user(self):
|
||||
"""
|
||||
No user.
|
||||
@ -414,9 +428,9 @@ class BaseActionTests(TestCase):
|
||||
setup_temp_cache({}, {})
|
||||
|
||||
task = Task.objects.create(
|
||||
ip_address="0.0.0.0", keystone_user={'roles': ['admin',
|
||||
'project_mod'],
|
||||
'project_id': 'test_project_id'})
|
||||
ip_address="0.0.0.0", keystone_user={
|
||||
'roles': ['admin', 'project_mod'],
|
||||
'project_id': 'test_project_id'})
|
||||
|
||||
data = {
|
||||
'email': 'test@example.com',
|
||||
@ -435,7 +449,8 @@ class BaseActionTests(TestCase):
|
||||
action.submit(token_data)
|
||||
self.assertEquals(action.valid, False)
|
||||
|
||||
@mock.patch('stacktask.base.models.user_store.IdentityManager', FakeManager)
|
||||
@mock.patch('stacktask.actions.models.user_store.IdentityManager',
|
||||
FakeManager)
|
||||
def test_edit_user_add(self):
|
||||
"""
|
||||
Add roles to existing user.
|
||||
@ -480,7 +495,8 @@ class BaseActionTests(TestCase):
|
||||
self.assertEquals(set(project.roles[user.name]),
|
||||
set(['Member', 'project_mod']))
|
||||
|
||||
@mock.patch('stacktask.base.models.user_store.IdentityManager', FakeManager)
|
||||
@mock.patch('stacktask.actions.models.user_store.IdentityManager',
|
||||
FakeManager)
|
||||
def test_edit_user_add_complete(self):
|
||||
"""
|
||||
Add roles to existing user.
|
||||
@ -526,7 +542,8 @@ class BaseActionTests(TestCase):
|
||||
self.assertEquals(set(project.roles[user.name]),
|
||||
set(['Member', 'project_mod']))
|
||||
|
||||
@mock.patch('stacktask.base.models.user_store.IdentityManager', FakeManager)
|
||||
@mock.patch('stacktask.actions.models.user_store.IdentityManager',
|
||||
FakeManager)
|
||||
def test_edit_user_remove(self):
|
||||
"""
|
||||
Remove roles from existing user.
|
||||
@ -570,7 +587,8 @@ class BaseActionTests(TestCase):
|
||||
|
||||
self.assertEquals(project.roles[user.name], ['Member'])
|
||||
|
||||
@mock.patch('stacktask.base.models.user_store.IdentityManager', FakeManager)
|
||||
@mock.patch('stacktask.actions.models.user_store.IdentityManager',
|
||||
FakeManager)
|
||||
def test_edit_user_remove_complete(self):
|
||||
"""
|
||||
Remove roles from existing user.
|
@ -18,6 +18,7 @@ class Migration(migrations.Migration):
|
||||
fields=[
|
||||
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
|
||||
('notes', jsonfield.fields.JSONField(default={})),
|
||||
('error', models.BooleanField(default=False, db_index=True)),
|
||||
('created_on', models.DateTimeField(default=django.utils.timezone.now)),
|
||||
('acknowledged', models.BooleanField(default=False, db_index=True)),
|
||||
],
|
||||
@ -29,7 +30,7 @@ class Migration(migrations.Migration):
|
||||
('ip_address', models.GenericIPAddressField()),
|
||||
('keystone_user', jsonfield.fields.JSONField(default={})),
|
||||
('project_id', models.CharField(max_length=200, null=True, db_index=True)),
|
||||
('task_view', models.CharField(max_length=200, db_index=True)),
|
||||
('task_type', models.CharField(max_length=200, db_index=True)),
|
||||
('action_notes', jsonfield.fields.JSONField(default={})),
|
||||
('cancelled', models.BooleanField(default=False, db_index=True)),
|
||||
('approved', models.BooleanField(default=False, db_index=True)),
|
||||
|
@ -1,19 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import models, migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('api', '0001_initial'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RenameField(
|
||||
model_name='task',
|
||||
old_name='task_view',
|
||||
new_name='task_type',
|
||||
),
|
||||
]
|
@ -121,6 +121,7 @@ class Notification(models.Model):
|
||||
|
||||
notes = JSONField(default={})
|
||||
task = models.ForeignKey(Task)
|
||||
error = models.BooleanField(default=False, db_index=True)
|
||||
created_on = models.DateTimeField(default=timezone.now)
|
||||
acknowledged = models.BooleanField(default=False, db_index=True)
|
||||
|
||||
|
@ -12,9 +12,8 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from django.conf.urls import patterns, url, include
|
||||
from django.conf.urls import url, include
|
||||
|
||||
urlpatterns = patterns(
|
||||
'',
|
||||
urlpatterns = [
|
||||
url(r'^v1/', include('stacktask.api.v1.urls')),
|
||||
)
|
||||
]
|
||||
|
@ -11,6 +11,7 @@
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from django.utils import timezone
|
||||
from django.conf import settings
|
||||
from rest_framework.response import Response
|
||||
@ -18,7 +19,7 @@ from rest_framework.response import Response
|
||||
from stacktask.api.v1 import tasks
|
||||
from stacktask.api import utils
|
||||
from stacktask.api import models
|
||||
from stacktask.base import user_store
|
||||
from stacktask.actions import user_store
|
||||
|
||||
|
||||
class UserList(tasks.InviteUser):
|
||||
|
@ -13,7 +13,7 @@
|
||||
# under the License.
|
||||
|
||||
from rest_framework.response import Response
|
||||
from stacktask.base.user_store import IdentityManager
|
||||
from stacktask.actions.user_store import IdentityManager
|
||||
from stacktask.api.models import Task
|
||||
from django.utils import timezone
|
||||
from stacktask.api import utils
|
||||
@ -343,7 +343,8 @@ class InviteUser(TaskView):
|
||||
self.logger.info("(%s) - New AttachUser request." % timezone.now())
|
||||
|
||||
# Default project_id to the keystone user's project
|
||||
if 'project_id' not in request.data or request.data['project_id'] is None:
|
||||
if ('project_id' not in request.data or
|
||||
request.data['project_id'] is None):
|
||||
request.data['project_id'] = request.keystone_user['project_id']
|
||||
|
||||
# TODO: First check if the user already exists or is pending
|
||||
|
@ -139,7 +139,8 @@ class APITests(APITestCase):
|
||||
These tests also focus on authentication status
|
||||
and role prermissions."""
|
||||
|
||||
@mock.patch('stacktask.base.models.user_store.IdentityManager', FakeManager)
|
||||
@mock.patch('stacktask.actions.models.user_store.IdentityManager',
|
||||
FakeManager)
|
||||
def test_new_user(self):
|
||||
"""
|
||||
Ensure the new user workflow goes as expected.
|
||||
@ -173,7 +174,8 @@ class APITests(APITestCase):
|
||||
response = self.client.post(url, data, format='json')
|
||||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||
|
||||
@mock.patch('stacktask.base.models.user_store.IdentityManager', FakeManager)
|
||||
@mock.patch('stacktask.actions.models.user_store.IdentityManager',
|
||||
FakeManager)
|
||||
def test_new_user_no_project(self):
|
||||
"""
|
||||
Can't create a user for a non-existent project.
|
||||
@ -195,7 +197,8 @@ class APITests(APITestCase):
|
||||
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
|
||||
self.assertEqual(response.data, {'errors': ['actions invalid']})
|
||||
|
||||
@mock.patch('stacktask.base.models.user_store.IdentityManager', FakeManager)
|
||||
@mock.patch('stacktask.actions.models.user_store.IdentityManager',
|
||||
FakeManager)
|
||||
def test_new_user_not_my_project(self):
|
||||
"""
|
||||
Can't create a user for project that isn't mine.
|
||||
@ -216,7 +219,8 @@ class APITests(APITestCase):
|
||||
response = self.client.post(url, data, format='json', headers=headers)
|
||||
self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)
|
||||
|
||||
@mock.patch('stacktask.base.models.user_store.IdentityManager', FakeManager)
|
||||
@mock.patch('stacktask.actions.models.user_store.IdentityManager',
|
||||
FakeManager)
|
||||
def test_new_user_not_authenticated(self):
|
||||
"""
|
||||
Can't create a user if unauthenticated.
|
||||
@ -235,7 +239,8 @@ class APITests(APITestCase):
|
||||
{'errors': ["Credentials incorrect or none given."]}
|
||||
)
|
||||
|
||||
@mock.patch('stacktask.base.models.user_store.IdentityManager', FakeManager)
|
||||
@mock.patch('stacktask.actions.models.user_store.IdentityManager',
|
||||
FakeManager)
|
||||
def test_add_user_existing(self):
|
||||
"""
|
||||
Adding existing user to project.
|
||||
@ -273,7 +278,8 @@ class APITests(APITestCase):
|
||||
response = self.client.post(url, data, format='json')
|
||||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||
|
||||
@mock.patch('stacktask.base.models.user_store.IdentityManager', FakeManager)
|
||||
@mock.patch('stacktask.actions.models.user_store.IdentityManager',
|
||||
FakeManager)
|
||||
def test_add_user_existing_with_role(self):
|
||||
"""
|
||||
Adding existing user to project.
|
||||
@ -309,8 +315,10 @@ class APITests(APITestCase):
|
||||
response.data,
|
||||
{'notes': 'Task completed successfully.'})
|
||||
|
||||
@mock.patch('stacktask.base.models.user_store.IdentityManager', FakeManager)
|
||||
@mock.patch('stacktask.tenant_setup.models.IdentityManager', FakeManager)
|
||||
@mock.patch('stacktask.actions.models.user_store.IdentityManager',
|
||||
FakeManager)
|
||||
@mock.patch('stacktask.actions.tenant_setup.models.IdentityManager',
|
||||
FakeManager)
|
||||
def test_new_project(self):
|
||||
"""
|
||||
Ensure the new project workflow goes as expected.
|
||||
@ -343,8 +351,10 @@ class APITests(APITestCase):
|
||||
response = self.client.post(url, data, format='json')
|
||||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||
|
||||
@mock.patch('stacktask.base.models.user_store.IdentityManager', FakeManager)
|
||||
@mock.patch('stacktask.tenant_setup.models.IdentityManager', FakeManager)
|
||||
@mock.patch('stacktask.actions.models.user_store.IdentityManager',
|
||||
FakeManager)
|
||||
@mock.patch('stacktask.actions.tenant_setup.models.IdentityManager',
|
||||
FakeManager)
|
||||
def test_new_project_existing(self):
|
||||
"""
|
||||
Test to ensure validation marks actions as invalid
|
||||
@ -387,8 +397,10 @@ class APITests(APITestCase):
|
||||
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
|
||||
self.assertEqual(response.data, {'errors': ['actions invalid']})
|
||||
|
||||
@mock.patch('stacktask.base.models.user_store.IdentityManager', FakeManager)
|
||||
@mock.patch('stacktask.tenant_setup.models.IdentityManager', FakeManager)
|
||||
@mock.patch('stacktask.actions.models.user_store.IdentityManager',
|
||||
FakeManager)
|
||||
@mock.patch('stacktask.actions.tenant_setup.models.IdentityManager',
|
||||
FakeManager)
|
||||
def test_new_project_existing_user(self):
|
||||
"""
|
||||
Project created if not present, existing user attached.
|
||||
@ -434,7 +446,8 @@ class APITests(APITestCase):
|
||||
{'notes': 'Task completed successfully.'}
|
||||
)
|
||||
|
||||
@mock.patch('stacktask.base.models.user_store.IdentityManager', FakeManager)
|
||||
@mock.patch('stacktask.actions.models.user_store.IdentityManager',
|
||||
FakeManager)
|
||||
def test_reset_user(self):
|
||||
"""
|
||||
Ensure the reset user workflow goes as expected.
|
||||
@ -462,7 +475,8 @@ class APITests(APITestCase):
|
||||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||
self.assertEqual(user.password, 'new_test_password')
|
||||
|
||||
@mock.patch('stacktask.base.models.user_store.IdentityManager', FakeManager)
|
||||
@mock.patch('stacktask.actions.models.user_store.IdentityManager',
|
||||
FakeManager)
|
||||
def test_reset_user_no_existing(self):
|
||||
"""
|
||||
Actions should be invalid.
|
||||
@ -534,7 +548,8 @@ class APITests(APITestCase):
|
||||
self.assertEqual(
|
||||
response.data, {'errors': ['No task with this id.']})
|
||||
|
||||
@mock.patch('stacktask.base.models.user_store.IdentityManager', FakeManager)
|
||||
@mock.patch('stacktask.actions.models.user_store.IdentityManager',
|
||||
FakeManager)
|
||||
def test_token_expired_post(self):
|
||||
"""
|
||||
Expired token should do nothing, then delete itself.
|
||||
@ -566,7 +581,8 @@ class APITests(APITestCase):
|
||||
{'errors': ['This token does not exist or has expired.']})
|
||||
self.assertEqual(0, Token.objects.count())
|
||||
|
||||
@mock.patch('stacktask.base.models.user_store.IdentityManager', FakeManager)
|
||||
@mock.patch('stacktask.actions.models.user_store.IdentityManager',
|
||||
FakeManager)
|
||||
def test_token_expired_get(self):
|
||||
"""
|
||||
Expired token should do nothing, then delete itself.
|
||||
@ -597,8 +613,10 @@ class APITests(APITestCase):
|
||||
{'errors': ['This token does not exist or has expired.']})
|
||||
self.assertEqual(0, Token.objects.count())
|
||||
|
||||
@mock.patch('stacktask.base.models.user_store.IdentityManager', FakeManager)
|
||||
@mock.patch('stacktask.tenant_setup.models.IdentityManager', FakeManager)
|
||||
@mock.patch('stacktask.actions.models.user_store.IdentityManager',
|
||||
FakeManager)
|
||||
@mock.patch('stacktask.actions.tenant_setup.models.IdentityManager',
|
||||
FakeManager)
|
||||
def test_task_complete(self):
|
||||
"""
|
||||
Can't approve a completed task.
|
||||
@ -629,8 +647,10 @@ class APITests(APITestCase):
|
||||
response.data,
|
||||
{'errors': ['This task has already been completed.']})
|
||||
|
||||
@mock.patch('stacktask.base.models.user_store.IdentityManager', FakeManager)
|
||||
@mock.patch('stacktask.tenant_setup.models.IdentityManager', FakeManager)
|
||||
@mock.patch('stacktask.actions.models.user_store.IdentityManager',
|
||||
FakeManager)
|
||||
@mock.patch('stacktask.actions.tenant_setup.models.IdentityManager',
|
||||
FakeManager)
|
||||
def test_task_update(self):
|
||||
"""
|
||||
Creates a invalid task.
|
||||
@ -680,8 +700,10 @@ class APITests(APITestCase):
|
||||
response.data,
|
||||
{'notes': ['created token']})
|
||||
|
||||
@mock.patch('stacktask.base.models.user_store.IdentityManager', FakeManager)
|
||||
@mock.patch('stacktask.tenant_setup.models.IdentityManager', FakeManager)
|
||||
@mock.patch('stacktask.actions.models.user_store.IdentityManager',
|
||||
FakeManager)
|
||||
@mock.patch('stacktask.actions.tenant_setup.models.IdentityManager',
|
||||
FakeManager)
|
||||
def test_notification_createproject(self):
|
||||
"""
|
||||
CreateProject should create a notification.
|
||||
@ -712,8 +734,10 @@ class APITests(APITestCase):
|
||||
response.data[0]['task'],
|
||||
new_task.uuid)
|
||||
|
||||
@mock.patch('stacktask.base.models.user_store.IdentityManager', FakeManager)
|
||||
@mock.patch('stacktask.tenant_setup.models.IdentityManager', FakeManager)
|
||||
@mock.patch('stacktask.actions.models.user_store.IdentityManager',
|
||||
FakeManager)
|
||||
@mock.patch('stacktask.actions.tenant_setup.models.IdentityManager',
|
||||
FakeManager)
|
||||
def test_notification_acknowledge(self):
|
||||
"""
|
||||
Test that you can acknowledge a notification.
|
||||
@ -753,8 +777,10 @@ class APITests(APITestCase):
|
||||
response = self.client.get(url, headers=headers)
|
||||
self.assertEqual(response.data, [])
|
||||
|
||||
@mock.patch('stacktask.base.models.user_store.IdentityManager', FakeManager)
|
||||
@mock.patch('stacktask.tenant_setup.models.IdentityManager', FakeManager)
|
||||
@mock.patch('stacktask.actions.models.user_store.IdentityManager',
|
||||
FakeManager)
|
||||
@mock.patch('stacktask.actions.tenant_setup.models.IdentityManager',
|
||||
FakeManager)
|
||||
def test_notification_acknowledge_list(self):
|
||||
"""
|
||||
Test that you can acknowledge a list of notifications.
|
||||
@ -792,7 +818,8 @@ class APITests(APITestCase):
|
||||
response = self.client.get(url, headers=headers)
|
||||
self.assertEqual(response.data, [])
|
||||
|
||||
@mock.patch('stacktask.base.models.user_store.IdentityManager', FakeManager)
|
||||
@mock.patch('stacktask.actions.models.user_store.IdentityManager',
|
||||
FakeManager)
|
||||
def test_token_expired_delete(self):
|
||||
"""
|
||||
test deleting of expired tokens.
|
||||
@ -847,7 +874,8 @@ class APITests(APITestCase):
|
||||
{'notes': ['Deleted all expired tokens.']})
|
||||
self.assertEqual(Token.objects.count(), 1)
|
||||
|
||||
@mock.patch('stacktask.base.models.user_store.IdentityManager', FakeManager)
|
||||
@mock.patch('stacktask.actions.models.user_store.IdentityManager',
|
||||
FakeManager)
|
||||
def test_token_reissue(self):
|
||||
"""
|
||||
test for reissue of tokens
|
||||
@ -891,8 +919,10 @@ class APITests(APITestCase):
|
||||
new_token = Token.objects.all()[0]
|
||||
self.assertNotEquals(new_token.token, uuid)
|
||||
|
||||
@mock.patch('stacktask.base.models.user_store.IdentityManager', FakeManager)
|
||||
@mock.patch('stacktask.tenant_setup.models.IdentityManager', FakeManager)
|
||||
@mock.patch('stacktask.actions.models.user_store.IdentityManager',
|
||||
FakeManager)
|
||||
@mock.patch('stacktask.actions.tenant_setup.models.IdentityManager',
|
||||
FakeManager)
|
||||
def test_cancel_task(self):
|
||||
"""
|
||||
Ensure the ability to cancel a task.
|
||||
@ -927,8 +957,10 @@ class APITests(APITestCase):
|
||||
headers=headers)
|
||||
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
@mock.patch('stacktask.base.models.user_store.IdentityManager', FakeManager)
|
||||
@mock.patch('stacktask.tenant_setup.models.IdentityManager', FakeManager)
|
||||
@mock.patch('stacktask.actions.models.user_store.IdentityManager',
|
||||
FakeManager)
|
||||
@mock.patch('stacktask.actions.tenant_setup.models.IdentityManager',
|
||||
FakeManager)
|
||||
def test_cancel_task_sent_token(self):
|
||||
"""
|
||||
Ensure the ability to cancel a task after the token is sent.
|
||||
@ -965,8 +997,10 @@ class APITests(APITestCase):
|
||||
response = self.client.post(url, data, format='json')
|
||||
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
@mock.patch('stacktask.base.models.user_store.IdentityManager', FakeManager)
|
||||
@mock.patch('stacktask.tenant_setup.models.IdentityManager', FakeManager)
|
||||
@mock.patch('stacktask.actions.models.user_store.IdentityManager',
|
||||
FakeManager)
|
||||
@mock.patch('stacktask.actions.tenant_setup.models.IdentityManager',
|
||||
FakeManager)
|
||||
def test_task_update_unapprove(self):
|
||||
"""
|
||||
Ensure task update doesn't work for approved actions.
|
||||
|
@ -12,14 +12,13 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from django.conf.urls import patterns, url
|
||||
from django.conf.urls import url
|
||||
from stacktask.api.v1 import views
|
||||
from stacktask.api.v1 import tasks
|
||||
from stacktask.api.v1 import openstack
|
||||
|
||||
|
||||
urlpatterns = patterns(
|
||||
'',
|
||||
urlpatterns = [
|
||||
url(r'^tasks/(?P<uuid>\w+)/?$', views.TaskDetail.as_view()),
|
||||
url(r'^tasks/?$', views.TaskList.as_view()),
|
||||
url(r'^tokens/(?P<id>\w+)', views.TokenDetail.as_view()),
|
||||
@ -37,4 +36,4 @@ urlpatterns = patterns(
|
||||
url(r'^openstack/users/(?P<user_id>\w+)/?$', openstack.UserDetail.as_view()),
|
||||
url(r'^openstack/users/?$', openstack.UserList.as_view()),
|
||||
url(r'^openstack/roles/?$', openstack.RoleList.as_view()),
|
||||
)
|
||||
]
|
||||
|
@ -5,7 +5,7 @@ from uuid import uuid4
|
||||
from django.core.mail import send_mail
|
||||
from smtplib import SMTPException
|
||||
from django.conf import settings
|
||||
from django.template import loader, Context
|
||||
from django.template import loader
|
||||
|
||||
|
||||
def create_token(task):
|
||||
@ -61,8 +61,8 @@ def send_email(registration, email_conf, token=None):
|
||||
context = {'registration': registration, 'actions': actions}
|
||||
|
||||
try:
|
||||
message = template.render(Context(context))
|
||||
html_message = html_template.render(Context(context))
|
||||
message = template.render(context)
|
||||
html_message = html_template.render(context)
|
||||
send_mail(
|
||||
email_conf['subject'], message, email_conf['reply'],
|
||||
[emails.pop()], fail_silently=False, html_message=html_message)
|
||||
@ -72,14 +72,22 @@ def send_email(registration, email_conf, token=None):
|
||||
("Error: '%s' while emailing token for registration: %s" %
|
||||
(e, registration.uuid))
|
||||
}
|
||||
create_notification(registration, notes)
|
||||
create_notification(registration, notes, error=True)
|
||||
# TODO(adriant): raise some error?
|
||||
# and surround calls to this function with try/except
|
||||
|
||||
|
||||
def create_notification(task, notes):
|
||||
def create_notification(task, notes, error=False):
|
||||
notification = Notification.objects.create(
|
||||
task=task,
|
||||
notes=notes
|
||||
notes=notes,
|
||||
error=error
|
||||
)
|
||||
notification.save()
|
||||
|
||||
class_conf = settings.TASK_SETTINGS[task.task_type]
|
||||
|
||||
# NOTE(adriant): some form of error handling is probably needed:
|
||||
for note_engine, conf in class_conf.get('notifications', {}).iteritems():
|
||||
engine = settings.NOTIFICATION_ENGINES[note_engine](conf)
|
||||
engine.notify(task, notes, error)
|
||||
|
0
stacktask/notifications/__init__.py
Normal file
0
stacktask/notifications/__init__.py
Normal file
0
stacktask/notifications/migrations/__init__.py
Normal file
0
stacktask/notifications/migrations/__init__.py
Normal file
77
stacktask/notifications/models.py
Normal file
77
stacktask/notifications/models.py
Normal file
@ -0,0 +1,77 @@
|
||||
# Copyright (C) 2015 Catalyst IT Ltd
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from django.conf import settings
|
||||
from django.core.mail import send_mail
|
||||
from django.template import loader
|
||||
|
||||
|
||||
class NotificationEngine(object):
|
||||
""""""
|
||||
|
||||
def __init__(self, conf):
|
||||
self.conf = conf
|
||||
|
||||
def notify(self, task, notes, error):
|
||||
return self._notify(task, notes, error)
|
||||
|
||||
def _notify(self, task, notes, error):
|
||||
raise NotImplementedError
|
||||
|
||||
|
||||
class EmailNotification(NotificationEngine):
|
||||
"""
|
||||
Basic email notification engine. Will
|
||||
send an email in the given templates.
|
||||
|
||||
Example conf:
|
||||
<TaskView>:
|
||||
notifications:
|
||||
EmailNotification:
|
||||
emails:
|
||||
- example@example.com
|
||||
reply: no-reply@example.com
|
||||
template: notification.txt
|
||||
html_template: completed.txt
|
||||
<other notification>:
|
||||
...
|
||||
"""
|
||||
|
||||
def notify(self, task, notes, error):
|
||||
return self._notify(task, notes, error)
|
||||
|
||||
def _notify(self, task, notes, error):
|
||||
template = loader.get_template(self.conf['template'])
|
||||
html_template = loader.get_template(self.conf['html_template'])
|
||||
|
||||
context = {'task': task, 'notes': notes}
|
||||
|
||||
# NOTE(adriant): Error handling?
|
||||
message = template.render(context)
|
||||
html_message = html_template.render(context)
|
||||
if error:
|
||||
subject = "Error - %s notification" % task.task_type
|
||||
else:
|
||||
subject = "%s notification" % task.task_type
|
||||
send_mail(
|
||||
subject, message, self.conf['reply'],
|
||||
self.conf['emails'], fail_silently=False,
|
||||
html_message=html_message)
|
||||
|
||||
|
||||
notification_engines = {
|
||||
'EmailNotification': EmailNotification,
|
||||
}
|
||||
|
||||
settings.NOTIFICATION_ENGINES.update(notification_engines)
|
0
stacktask/notifications/request_tracker/__init__.py
Normal file
0
stacktask/notifications/request_tracker/__init__.py
Normal file
74
stacktask/notifications/request_tracker/models.py
Normal file
74
stacktask/notifications/request_tracker/models.py
Normal file
@ -0,0 +1,74 @@
|
||||
# Copyright (C) 2015 Catalyst IT Ltd
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from django.conf import settings
|
||||
from django.template import loader
|
||||
from stacktask.notifications.models import NotificationEngine
|
||||
import rt
|
||||
|
||||
|
||||
class RTNotification(NotificationEngine):
|
||||
"""
|
||||
Request Tracker notification engine. Will
|
||||
create a new ticket in RT for the notification.
|
||||
|
||||
Example conf:
|
||||
<TaskView>:
|
||||
notifications:
|
||||
RTNotification:
|
||||
url: http://localhost/rt/REST/1.0/
|
||||
queue: helpdesk
|
||||
username: example@example.com
|
||||
password: password
|
||||
template: notification.txt
|
||||
<other notification>:
|
||||
...
|
||||
"""
|
||||
|
||||
def __init__(self, conf):
|
||||
super(RTNotification, self).__init__(conf)
|
||||
# in memory dict to be used for passing data between actions:
|
||||
tracker = rt.Rt(
|
||||
self.conf['url'], self.conf['username'], self.conf['password'])
|
||||
tracker.login()
|
||||
self.tracker = tracker
|
||||
|
||||
def notify(self, task, notes, error):
|
||||
return self._notify(task, notes, error)
|
||||
|
||||
def _notify(self, task, notes, error):
|
||||
template = loader.get_template(self.conf['template'])
|
||||
|
||||
context = {'task': task, 'notes': notes}
|
||||
|
||||
# NOTE(adriant): Error handling?
|
||||
message = template.render(context)
|
||||
|
||||
if error:
|
||||
subject = "Error - %s notification" % task.task_type
|
||||
else:
|
||||
subject = "%s notification" % task.task_type
|
||||
|
||||
self.tracker.create_ticket(
|
||||
Queue=self.conf['queue'], Subject=subject,
|
||||
# newline + space tells the RT api to actually treat it like a
|
||||
# newline.
|
||||
Text=message.replace('\n', '\n '))
|
||||
|
||||
|
||||
notification_engines = {
|
||||
'RTNotification': RTNotification,
|
||||
}
|
||||
|
||||
settings.NOTIFICATION_ENGINES.update(notification_engines)
|
3
stacktask/notifications/request_tracker/tests.py
Normal file
3
stacktask/notifications/request_tracker/tests.py
Normal file
@ -0,0 +1,3 @@
|
||||
from django.test import TestCase
|
||||
|
||||
# Create your tests here.
|
10
stacktask/notifications/templates/notification.txt
Normal file
10
stacktask/notifications/templates/notification.txt
Normal file
@ -0,0 +1,10 @@
|
||||
Hello,
|
||||
|
||||
task:
|
||||
{{ task }}
|
||||
|
||||
notes:
|
||||
{{ notes }}
|
||||
|
||||
Thank you for using our service.
|
||||
|
3
stacktask/notifications/tests.py
Normal file
3
stacktask/notifications/tests.py
Normal file
@ -0,0 +1,3 @@
|
||||
from django.test import TestCase
|
||||
|
||||
# Create your tests here.
|
@ -46,8 +46,9 @@ INSTALLED_APPS = (
|
||||
'django.contrib.staticfiles',
|
||||
'rest_framework',
|
||||
'rest_framework_swagger',
|
||||
'stacktask.base',
|
||||
'stacktask.actions',
|
||||
'stacktask.api',
|
||||
'stacktask.notifications',
|
||||
)
|
||||
|
||||
MIDDLEWARE_CLASSES = (
|
||||
@ -152,3 +153,5 @@ ACTION_SETTINGS = CONFIG['ACTION_SETTINGS']
|
||||
# Dict of actions and their serializers.
|
||||
# - This is populated from the various model modules at startup:
|
||||
ACTION_CLASSES = {}
|
||||
|
||||
NOTIFICATION_ENGINES = {}
|
||||
|
@ -16,7 +16,7 @@ SECRET_KEY = '+er!!4olta#17a=n%uotcazg2ncpl==yjog%1*o-(cr%zys-)!'
|
||||
|
||||
ADDITIONAL_APPS = [
|
||||
'stacktask.api.v1',
|
||||
'stacktask.tenant_setup'
|
||||
'stacktask.actions.tenant_setup'
|
||||
]
|
||||
|
||||
DATABASES = {
|
||||
|
@ -12,13 +12,12 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from django.conf.urls import patterns, include, url
|
||||
from django.conf.urls import include, url
|
||||
from django.conf import settings
|
||||
|
||||
urlpatterns = patterns(
|
||||
'',
|
||||
urlpatterns = [
|
||||
url(r'^', include('stacktask.api.urls')),
|
||||
)
|
||||
]
|
||||
|
||||
if settings.DEBUG:
|
||||
urlpatterns.append(url(r'^docs/', include('rest_framework_swagger.urls')))
|
||||
|
Loading…
x
Reference in New Issue
Block a user