Updated ROADMAP

This commit is contained in:
Marc 2014-09-28 12:28:57 +00:00
parent ccf50d0515
commit 6874060fc3
11 changed files with 95 additions and 51 deletions

View File

@ -1,45 +1,58 @@
# Roadmap # Roadmap
### 1.0a1 Milestone (first alpha release on Sep '14) ### 1.0a1 Milestone (first alpha release on Oct '14)
1. [x] Automated deployment of the development environment 1. [x] Automated deployment of the development environment
2. [x] Automated installation and upgrading 2. [x] Automated installation and upgrading
2. [ ] Testing framework for running unittests and functional tests 2. [ ] Testing framework for running unittests and functional tests with LXC containers
2. [ ] Continuous integration environment 2. [ ] Continuous integration with Jenkins
2. [x] Admin interface based on django.contrib.admin foundations 2. [x] Admin interface based on django.contrib.admin
3. [x] REST API based on django-rest-framework foundations 3. [x] REST API for users
2. [x] [Orchestra-orm](https://github.com/glic3rinu/orchestra-orm) a Python library for easily interacting with the REST API 2. [x] [Orchestra-orm](https://github.com/glic3rinu/orchestra-orm) a Python library for easily interacting with the REST API
3. [x] Service orchestration framework 3. [x] Service orchestration framework
4. [ ] Data model, input validation, admin and REST interfaces, permissions, unit and functional tests, service management, migration scripts and some documentation of: 4. [ ] Data model, input validation, admin and REST interfaces, permissions, unit and functional tests, service management, migration scripts and documentation of:
1. [x] Web applications 1. [x] PHP/static Web applications
2. [ ] FTP accounts 1. [x] Websites with Apache
2. [ ] Databases 2. [-] FTP/rsync/scp/shell system accounts
1. [ ] Mail accounts, aliases, forwards 2. [-] Databases and database users
1. [x] DNS 1. [-] Mail accounts, aliases, forwards with Postfix and Dovecot
1. [ ] Mailing lists 1. [x] DNS with Bind
1. [-] Mailing lists with Mailman
1. [x] Contact management and service contraction 1. [x] Contact management and service contraction
1. [ ] Object level permissions system 1. [-] Unittests of the bussines logic logic
1. [ ] Unittests of all the logic 2. [-] Functional tests of all Admin UI and REST interations
2. [ ] Functional tests of all Admin and REST interations
1. [ ] Initial documentation 1. [ ] Initial documentation
### 1.0b1 Milestone (first beta release on Nov '14) ### 1.0b1 Milestone (first beta release on Dec '14)
1. [x] Resource monitoring 1. [x] Resource allocation and monitoring
1. [ ] Orders 1. [x] Order tracking
2. [ ] Pricing 2. [x] Service definition, service plans and pricing
3. [ ] Billing 3. [-] Billing
1. [ ] Payment methods 3. [x] Invoice
2. [ ] Scheduling of service cancellations and deactivations 3. [x] Membership fee
3. [-] Amendment invoice
3. [-] Amendment fee
3. [x] Pro Forma
3. [ ] Advanced bill handling (move lines, undo billing, ...)
1. [x] Payment methods
1. [x] SEPA Direct Debit
2. [x] SEPA Credit Transfer
1. [ ] Full documentation 1. [ ] Full documentation
2. [-] Additional services
2. [-] VPS with Proxmox/OpenVZ
2. [-] SaaS (Software as a Service) Redmine/phpList/BSCW/Wordpress/Moodle/Drupal
2. [x] Miscellaneous services
2. [x] Issue tracking system
### 1.0 Milestone (first stable release on Feb '15) ### 1.0 Milestone (first stable release on Apr '15)
1. [ ] Stabilize data model, internal APIs and REST API 1. [ ] Stabilize data model, internal APIs and REST API
1. [ ] Integration with third-party service providers, e.g. Gandi 1. [ ] Integration with third-party service providers, e.g. Gandi
1. [ ] Support for additional services like VPS
2. [ ] Issue tracking system
3. [ ] Translation to Spanish and Catalan 3. [ ] Translation to Spanish and Catalan
2. [ ] Scheduling of service cancellations and deactivations
1. [ ] Object level permissions system
2. [ ] API access for superusers

18
TODO.md
View File

@ -76,7 +76,6 @@ Remember that, as always with QuerySets, any subsequent chained methods which im
* help_text on readonly_fields specialy Bill.state. (eg. A bill is in OPEN state when bla bla ) * help_text on readonly_fields specialy Bill.state. (eg. A bill is in OPEN state when bla bla )
* Transaction states: CREATED, PROCESSED, EXECUTED, COMMITED, ABORTED (SECURED, REJECTED?) * Transaction states: CREATED, PROCESSED, EXECUTED, COMMITED, ABORTED (SECURED, REJECTED?)
* bill.send() -> transacction.EXECUTED when source=None * bill.send() -> transacction.EXECUTED when source=None
* transaction.secured() -> bill.paid when bill.total == transaction.value else Error * transaction.secured() -> bill.paid when bill.total == transaction.value else Error
@ -84,8 +83,8 @@ Remember that, as always with QuerySets, any subsequent chained methods which im
* bill.bad_debt() -> transaction.ABORTED * bill.bad_debt() -> transaction.ABORTED
* transaction.ABORTED -> bill.bad_debt * transaction.ABORTED -> bill.bad_debt
- Issue new transaction when current transaction is ABORTED - Issue new transaction when current transaction is ABORTED
* underescore *every* private function
* underescore *every* private function
* create log file at /var/log/orchestra.log and rotate * create log file at /var/log/orchestra.log and rotate
@ -94,7 +93,6 @@ Remember that, as always with QuerySets, any subsequent chained methods which im
def register_on(self): def register_on(self):
return order.register_at.date() return order.register_at.date()
* mail backend related_models = ('resources__content_type') ?? * mail backend related_models = ('resources__content_type') ??
* ignore orders * ignore orders
@ -103,3 +101,17 @@ Remember that, as always with QuerySets, any subsequent chained methods which im
* Domain backend PowerDNS Bind validation support? * Domain backend PowerDNS Bind validation support?
* Maildir billing tests/ webdisk billing tests (avg metric) * Maildir billing tests/ webdisk billing tests (avg metric)
* move icons to apps, and use appconfig to cleanup config stuff
* when using modeladmin to store shit like self.account, make sure to have a cleanslate in each request
*jabber with mailbox accounts (dovecto mail notification)
* rename accounts register to manager register
* make accounts django auth users
- when an account is created a mirrored system user is created
- system users are independent users, so they can have different passwords and all.
* take a look icons from ajenti ;)

View File

@ -4,6 +4,8 @@ from django.contrib import admin
from django.contrib.admin.utils import unquote from django.contrib.admin.utils import unquote
from django.forms.models import BaseInlineFormSet from django.forms.models import BaseInlineFormSet
from django.shortcuts import render, redirect from django.shortcuts import render, redirect
from django.utils.text import camel_case_to_spaces
from django.utils.translation import ugettext_lazy as _
from .utils import set_url_query, action_to_view, wrap_admin_view from .utils import set_url_query, action_to_view, wrap_admin_view
@ -175,11 +177,24 @@ class SelectPluginAdminMixin(object):
self.plugin_value = plugin_value self.plugin_value = plugin_value
if not plugin_value: if not plugin_value:
self.plugin_value = self.plugin.get_plugins()[0] self.plugin_value = self.plugin.get_plugins()[0]
return super(SelectPluginAdminMixin, self).add_view(request, context = {
form_url=form_url, extra_context=extra_context) 'title': _("Add new %s") % camel_case_to_spaces(self.plugin_value),
# TODO add plugin name on title }
context.update(extra_context or {})
return super(SelectPluginAdminMixin, self).add_view(request, form_url=form_url,
extra_context=context)
return redirect('./select-plugin/?%s' % request.META['QUERY_STRING']) return redirect('./select-plugin/?%s' % request.META['QUERY_STRING'])
def change_view(self, request, object_id, form_url='', extra_context=None):
obj = self.get_object(request, unquote(object_id))
plugin_value = getattr(obj, self.plugin_field)
context = {
'title': _("Change %s") % camel_case_to_spaces(plugin_value),
}
context.update(extra_context or {})
return super(SelectPluginAdminMixin, self).change_view(request, object_id,
form_url=form_url, extra_context=context)
def save_model(self, request, obj, form, change): def save_model(self, request, obj, form, change):
if not change: if not change:
setattr(obj, self.plugin_field, self.plugin_value) setattr(obj, self.plugin_field, self.plugin_value)

View File

@ -64,11 +64,12 @@ class ResourceAdmin(ExtendedModelAdmin):
class ResourceDataAdmin(admin.ModelAdmin): class ResourceDataAdmin(admin.ModelAdmin):
list_display = ( list_display = (
'id', 'resource', 'used', 'allocated', 'updated_at', 'content_object_link' 'id', 'resource_link', 'used', 'allocated', 'updated_at', 'content_object_link'
) )
list_filter = ('resource',) list_filter = ('resource',)
readonly_fields = ('content_object_link',) readonly_fields = ('content_object_link',)
resource_link = admin_link('resource')
content_object_link = admin_link('content_object') content_object_link = admin_link('content_object')
def get_queryset(self, request): def get_queryset(self, request):

View File

@ -18,6 +18,9 @@ class SaaS(models.Model):
verbose_name = "SaaS" verbose_name = "SaaS"
verbose_name_plural = "SaaS" verbose_name_plural = "SaaS"
def __unicode__(self):
return "%s (%s)" % (self.description, self.service_class.verbose_name)
@cached_property @cached_property
def service_class(self): def service_class(self):
return SoftwareService.get_plugin(self.service) return SoftwareService.get_plugin(self.service)

View File

@ -1,10 +1,14 @@
from django import forms from django import forms
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from .options import SoftwareService, SoftwareServiceForm from orchestra.forms import PluginDataForm
from .options import SoftwareService
class BSCWForm(SoftwareServiceForm): class BSCWForm(PluginDataForm):
username = forms.CharField(label=_("Username"), max_length=64)
password = forms.CharField(label=_("Password"), max_length=64)
quota = forms.IntegerField(label=_("Quota")) quota = forms.IntegerField(label=_("Quota"))

View File

@ -1,12 +1,16 @@
from django import forms from django import forms
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from .options import SoftwareService, SoftwareServiceForm from orchestra.forms import PluginDataForm
from .options import SoftwareService
class GitLabForm(SoftwareServiceForm): class GitLabForm(PluginDataForm):
username = forms.CharField(label=_("Username"), max_length=64)
password = forms.CharField(label=_("Password"), max_length=64)
project_name = forms.CharField(label=_("Project name"), max_length=64) project_name = forms.CharField(label=_("Project name"), max_length=64)
email = forms.CharField(label=_("Email"), max_length=64) email = forms.EmailField(label=_("Email"))
class GitLabService(SoftwareService): class GitLabService(SoftwareService):

View File

@ -1,7 +1,6 @@
from django import forms from django import forms
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from orchestra.forms import PluginDataForm
from orchestra.utils import plugins from orchestra.utils import plugins
from orchestra.utils.functional import cached from orchestra.utils.functional import cached
from orchestra.utils.python import import_class from orchestra.utils.python import import_class
@ -9,17 +8,9 @@ from orchestra.utils.python import import_class
from .. import settings from .. import settings
class SoftwareServiceForm(PluginDataForm):
username = forms.CharField(label=_("Username"), max_length=64)
password = forms.CharField(label=_("Password"), max_length=64)
class Meta:
exclude = ('data', 'service')
class SoftwareService(plugins.Plugin): class SoftwareService(plugins.Plugin):
description_field = '' description_field = ''
form = SoftwareServiceForm form = None
serializer = None serializer = None
@classmethod @classmethod

View File

@ -4,4 +4,5 @@ from django.conf import settings
SAAS_ENABLED_SERVICES = getattr(settings, 'SAAS_ENABLED_SERVICES', ( SAAS_ENABLED_SERVICES = getattr(settings, 'SAAS_ENABLED_SERVICES', (
'orchestra.apps.saas.services.bscw.BSCWService', 'orchestra.apps.saas.services.bscw.BSCWService',
'orchestra.apps.saas.services.gitlab.GitLabService', 'orchestra.apps.saas.services.gitlab.GitLabService',
'orchestra.apps.saas.services.phplist.PHPListService',
)) ))

View File

@ -37,9 +37,9 @@
{% if not is_popup %} {% if not is_popup %}
<!-- Header --> <!-- Header -->
{% block header-stetic %}<div id="header">{% endblock %} {% block header-stetic %}<div id="header"><div id="header-wrapper">{% endblock %}
<div id="branding"> <div id="branding">
{% block branding %}{% endblock %} {% block branding %}{% endblock %}
</div> </div>
{% if user.is_active and user.is_staff %} {% if user.is_active and user.is_staff %}
<div id="user-tools"> <div id="user-tools">
@ -69,7 +69,7 @@
</div> </div>
{% endif %} {% endif %}
{% block nav-global %}{% endblock %} {% block nav-global %}{% endblock %}
</div> </div></div>
<!-- END Header --> <!-- END Header -->
{% block breadcrumbs %}<div class="breadcrumbs"><a href="/">{% trans 'Home' %}</a>{% if title %} &rsaquo; {{ title }}{% endif %}</div>{% endblock %} {% block breadcrumbs %}<div class="breadcrumbs"><a href="/">{% trans 'Home' %}</a>{% if title %} &rsaquo; {{ title }}{% endif %}</div>{% endblock %}
{% endif %} {% endif %}

View File

@ -9,7 +9,7 @@
<div style="margin:20px;"> <div style="margin:20px;">
<ul> <ul>
{% for name, verbose in plugins %} {% for name, verbose in plugins %}
<li><a href="../?{{ field }}={{ name }}&{{ request.META.QUERY_STRING }}">{{ verbose }}</<a></li> <li><a style="font-size:small;" href="../?{{ field }}={{ name }}&{{ request.META.QUERY_STRING }}">{{ verbose }}</<a></li>
{% endfor %} {% endfor %}
</ul> </ul>
</div> </div>