diff --git a/dashboard/ReadMe.txt b/dashboard/ReadMe.txt index 6678e44f..8d5cc0da 100644 --- a/dashboard/ReadMe.txt +++ b/dashboard/ReadMe.txt @@ -1,6 +1,7 @@ # TO DO: -# 1. Remove extra code -# +# 1. Add new functional for services and data centers +# 2. Fix issue with list of services: services table shoudl show services for +# specific data center This file is described how to install new tab on horizon dashboard. We should do the following: diff --git a/dashboard/windc/forms.py b/dashboard/windc/forms.py index 5eb53dfe..7c1329fb 100644 --- a/dashboard/windc/forms.py +++ b/dashboard/windc/forms.py @@ -33,20 +33,20 @@ from horizon import messages LOG = logging.getLogger(__name__) -class UpdateInstance(forms.SelfHandlingForm): +class UpdateWinDC(forms.SelfHandlingForm): tenant_id = forms.CharField(widget=forms.HiddenInput) - instance = forms.CharField(widget=forms.HiddenInput) + data_center = forms.CharField(widget=forms.HiddenInput) name = forms.CharField(required=True) def handle(self, request, data): try: - server = api.nova.server_update(request, data['instance'], + server = api.nova.server_update(request, data['data_center'], data['name']) messages.success(request, - _('Instance "%s" updated.') % data['name']) + _('Data Center "%s" updated.') % data['name']) return server except: redirect = reverse("horizon:project:windc:index") exceptions.handle(request, - _('Unable to update instance.'), + _('Unable to update data center.'), redirect=redirect) diff --git a/dashboard/windc/panel.py b/dashboard/windc/panel.py index 92bad578..07311949 100644 --- a/dashboard/windc/panel.py +++ b/dashboard/windc/panel.py @@ -22,7 +22,7 @@ from openstack_dashboard.dashboards.project import dashboard class WinDC(horizon.Panel): - name = _("Windows Services") + name = _("Windows Data Centers") slug = 'windc' diff --git a/dashboard/windc/tables.py b/dashboard/windc/tables.py index 3875f06f..1c1811a3 100644 --- a/dashboard/windc/tables.py +++ b/dashboard/windc/tables.py @@ -14,6 +14,10 @@ # License for the specific language governing permissions and limitations # under the License. + +# TO DO: clear extra modules + + import logging from django import shortcuts @@ -37,98 +41,32 @@ from openstack_dashboard.dashboards.project.access_and_security \ LOG = logging.getLogger(__name__) -ACTIVE_STATES = ("ACTIVE",) - -POWER_STATES = { - 0: "NO STATE", - 1: "RUNNING", - 2: "SHUTDOWN", - 3: "FAILED", - 4: "BUILDING", -} - -PAUSE = 0 -UNPAUSE = 1 -SUSPEND = 0 -RESUME = 1 - - -def is_deleting(instance): - task_state = getattr(instance, "OS-EXT-STS:task_state", None) - if not task_state: - return False - return task_state.lower() == "deleting" - - -class RebootService(tables.BatchAction): - name = "reboot" - action_present = _("Reboot") - action_past = _("Rebooted") - data_type_singular = _("Service") - data_type_plural = _("Services") - classes = ('btn-danger', 'btn-reboot') - - def allowed(self, request, instance=None): - return ((instance.status in ACTIVE_STATES - or instance.status == 'SHUTOFF') - and not is_deleting(instance)) - - def action(self, request, obj_id): - api.nova.server_reboot(request, obj_id) - class CreateService(tables.LinkAction): name = "CreateService" - verbose_name = _("Create Windows Service") + verbose_name = _("Create Service") url = "horizon:project:windc:create" classes = ("btn-launch", "ajax-modal") def allowed(self, request, datum): - try: - limits = api.nova.tenant_absolute_limits(request, reserved=True) - - instances_available = limits['maxTotalInstances'] \ - - limits['totalInstancesUsed'] - cores_available = limits['maxTotalCores'] \ - - limits['totalCoresUsed'] - ram_available = limits['maxTotalRAMSize'] - limits['totalRAMUsed'] - - if instances_available <= 0 or cores_available <= 0 \ - or ram_available <= 0: - if "disabled" not in self.classes: - self.classes = [c for c in self.classes] + ['disabled'] - self.verbose_name = string_concat(self.verbose_name, ' ', - _("(Quota exceeded)")) - else: - self.verbose_name = _("Create Windows Service") - classes = [c for c in self.classes if c != "disabled"] - self.classes = classes - except: - LOG.exception("Failed to retrieve quota information") - # If we can't get the quota information, leave it to the - # API to check when launching - - return True # The action should always be displayed - - -class DeleteService(tables.BatchAction): - name = "DeleteService" - action_present = _("DeleteService") - action_past = _("Scheduled termination of") - data_type_singular = _("Service") - data_type_plural = _("Services") - classes = ('btn-danger', 'btn-terminate') - - def allowed(self, request, instance=None): - if instance: - # FIXME(gabriel): This is true in Essex, but in FOLSOM an instance - # can be terminated in any state. We should improve this error - # handling when LP bug 1037241 is implemented. - return instance.status not in ("PAUSED", "SUSPENDED") return True def action(self, request, obj_id): - api.nova.server_delete(request, obj_id) + # FIX ME + api.windc.datacenter.create_service(request, obj_id) + +class CreateDataCenter(tables.LinkAction): + name = "CreateDataCenter" + verbose_name = _("Create Windows Data Center") + url = "horizon:project:windc:create_dc" + classes = ("btn-launch", "ajax-modal") + + def allowed(self, request, datum): + return True + + def action(self, request, obj_id): + # FIX ME + api.windc.datacenter.create(request, obj_id) class EditService(tables.LinkAction): @@ -138,7 +76,15 @@ class EditService(tables.LinkAction): classes = ("ajax-modal", "btn-edit") def allowed(self, request, instance): - return not is_deleting(instance) + return True + +class ShowDataCenterServices(tables.LinkAction): + name = "edit" + verbose_name = _("Services") + url = "horizon:project:windc:services" + + def allowed(self, request, instance): + return True class UpdateRow(tables.Row): ajax = True @@ -150,45 +96,26 @@ class UpdateRow(tables.Row): return instance -def get_ips(instance): - template_name = 'project/windc/_instance_ips.html' - context = {"instance": instance} - return template.loader.render_to_string(template_name, context) +class WinDCTable(tables.DataTable): + TASK_STATUS_CHOICES = ( + (None, True), + ("none", True) + ) + STATUS_CHOICES = ( + ("active", True), + ("shutoff", True), + ("error", False), + ) + name = tables.Column("name", + link=("horizon:project:windc:services"), + verbose_name=_("Name")) - -def get_size(instance): - if hasattr(instance, "full_flavor"): - size_string = _("%(name)s | %(RAM)s RAM | %(VCPU)s VCPU " - "| %(disk)s Disk") - vals = {'name': instance.full_flavor.name, - 'RAM': sizeformat.mbformat(instance.full_flavor.ram), - 'VCPU': instance.full_flavor.vcpus, - 'disk': sizeformat.diskgbformat(instance.full_flavor.disk)} - return size_string % vals - return _("Not available") - - -def get_power_state(instance): - return POWER_STATES.get(getattr(instance, "OS-EXT-STS:power_state", 0), '') - - -STATUS_DISPLAY_CHOICES = ( - ("resize", "Resize/Migrate"), - ("verify_resize", "Confirm or Revert Resize/Migrate"), - ("revert_resize", "Revert Resize/Migrate"), -) - - -TASK_DISPLAY_CHOICES = ( - ("image_snapshot", "Snapshotting"), - ("resize_prep", "Preparing Resize or Migrate"), - ("resize_migrating", "Resizing or Migrating"), - ("resize_migrated", "Resized or Migrated"), - ("resize_finish", "Finishing Resize or Migrate"), - ("resize_confirming", "Confirming Resize or Nigrate"), - ("resize_reverting", "Reverting Resize or Migrate"), - ("unpausing", "Resuming"), -) + class Meta: + name = "windc" + verbose_name = _("Windows Data Centers") + row_class = UpdateRow + table_actions = (CreateDataCenter,) + row_actions = (ShowDataCenterServices,) class WinServicesTable(tables.DataTable): @@ -202,29 +129,12 @@ class WinServicesTable(tables.DataTable): ("error", False), ) name = tables.Column("name", - link=("horizon:project:windc:detail"), + link=("horizon:project:windc"), verbose_name=_("Name")) - ip = tables.Column(get_ips, verbose_name=_("IP Address")) - size = tables.Column(get_size, - verbose_name=_("Type"), - attrs={'data-type': 'type'}) - status = tables.Column("status", - filters=(title, replace_underscores), - verbose_name=_("Status"), - status=True, - status_choices=STATUS_CHOICES, - display_choices=STATUS_DISPLAY_CHOICES) - task = tables.Column("OS-EXT-STS:task_state", - verbose_name=_("Task"), - filters=(title, replace_underscores), - status=True, - status_choices=TASK_STATUS_CHOICES, - display_choices=TASK_DISPLAY_CHOICES) class Meta: - name = "windc" - verbose_name = _("Windows Services") - status_columns = ["status", "task"] + name = "services" + verbose_name = _("Services") row_class = UpdateRow - table_actions = (CreateService, DeleteService) - row_actions = (EditService, RebootService) + table_actions = (CreateService,) + row_actions = (EditService,) diff --git a/dashboard/windc/templates/windc/index.html b/dashboard/windc/templates/windc/index.html index 1ab27368..fa172abe 100644 --- a/dashboard/windc/templates/windc/index.html +++ b/dashboard/windc/templates/windc/index.html @@ -1,9 +1,9 @@ {% extends 'base.html' %} {% load i18n %} -{% block title %}{% trans "Windows Services" %}{% endblock %} +{% block title %}{% trans "Windows Data Centers" %}{% endblock %} {% block page_header %} - {% include "horizon/common/_page_header.html" with title=_("Windows Services") %} + {% include "horizon/common/_page_header.html" with title=_("Windows Data Centers") %} {% endblock page_header %} {% block main %} diff --git a/dashboard/windc/urls.py b/dashboard/windc/urls.py index 8f52b6fb..6654ed22 100644 --- a/dashboard/windc/urls.py +++ b/dashboard/windc/urls.py @@ -20,12 +20,15 @@ from django.conf.urls.defaults import patterns, url -from .views import IndexView, CreateWinServiceView +from .views import IndexView, CreateWinDCView, WinServices, CreateWinServiceView VIEW_MOD = 'openstack_dashboard.dashboards.project.windc.views' urlpatterns = patterns(VIEW_MOD, url(r'^$', IndexView.as_view(), name='index'), - url(r'^create$', CreateWinServiceView.as_view(), name='create') + url(r'^create$', CreateWinServiceView.as_view(), name='create'), + url(r'^create_dc$', CreateWinDCView.as_view(), name='create_dc'), + url(r'^(?P[^/]+)/$', WinServices.as_view(), + name='services') ) diff --git a/dashboard/windc/views.py b/dashboard/windc/views.py index c5854e03..5948af10 100644 --- a/dashboard/windc/views.py +++ b/dashboard/windc/views.py @@ -15,8 +15,7 @@ # 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. +# License for the specific language governing permissions and limitations# under the License. """ Views for managing instances. @@ -36,51 +35,61 @@ from horizon import tables from horizon import workflows from openstack_dashboard import api -from .tables import WinServicesTable -from .workflows import CreateWinService +from .tables import WinDCTable, WinServicesTable +from .tabs import WinServicesTab +from .workflows import CreateWinService, CreateWinDC LOG = logging.getLogger(__name__) class IndexView(tables.DataTableView): - table_class = WinServicesTable + table_class = WinDCTable template_name = 'project/windc/index.html' def get_data(self): # Gather our instances try: - instances = api.nova.server_list(self.request) + data_centers = api.windc.datacenter_list(self.request) except: - instances = [] + data_centers = [] exceptions.handle(self.request, - _('Unable to retrieve instances.')) - # Gather our flavors and correlate our instances to them - if instances: - try: - flavors = api.nova.flavor_list(self.request) - except: - flavors = [] - exceptions.handle(self.request, ignore=True) + _('Unable to retrieve data centers list.')) + return data_centers - full_flavors = SortedDict([(str(flavor.id), flavor) - for flavor in flavors]) - # Loop through instances to get flavor info. - for instance in instances: - try: - flavor_id = instance.flavor["id"] - if flavor_id in full_flavors: - instance.full_flavor = full_flavors[flavor_id] - else: - # If the flavor_id is not in full_flavors list, - # get it via nova api. - instance.full_flavor = api.nova.flavor_get( - self.request, flavor_id) - except: - msg = _('Unable to retrieve instance size information.') - exceptions.handle(self.request, msg) - return instances +class WinServices(tables.DataTableView): + table_class = WinServicesTable + template_name = 'project/windc/services.html' + + def get_context_data(self, **kwargs): + context = super(WinServices, self).get_context_data(**kwargs) + context["domain_controller_name"] = self.get_data()[0].name + return context + + def get_data(self): + try: + dc_id = self.kwargs['domain_controller_id'] + domain_controller = api.windc.datacenter_get(self.request, dc_id) + except: + redirect = reverse('horizon:project:windc:index') + exceptions.handle(self.request, + _('Unable to retrieve details for ' + 'domain_controller "%s".') % dc_id, + redirect=redirect) + self._domain_controller = [domain_controller,] + return self._domain_controller + + +class CreateWinDCView(workflows.WorkflowView): + workflow_class = CreateWinDC + template_name = "project/windc/create_dc.html" + + def get_initial(self): + initial = super(CreateWinDCView, self).get_initial() + initial['project_id'] = self.request.user.tenant_id + initial['user_id'] = self.request.user.id + return initial class CreateWinServiceView(workflows.WorkflowView): workflow_class = CreateWinService diff --git a/dashboard/windc/workflows.py b/dashboard/windc/workflows.py index 385da514..7db3e783 100644 --- a/dashboard/windc/workflows.py +++ b/dashboard/windc/workflows.py @@ -61,8 +61,22 @@ class SelectProjectUserAction(workflows.Action): class SelectProjectUser(workflows.Step): action_class = SelectProjectUserAction + #contributes = ("project_id", "user_id") +class ConfigureDCAction(workflows.Action): + dc_name = forms.CharField(label=_("Data Center Name"), + required=True, + help_text=_("A name of new data center.")) + + class Meta: + name = _("Data Center") + help_text_template = ("project/windc/_data_center_help.html") + + +class ConfigureDC(workflows.Step): + action_class = ConfigureDCAction + class ConfigureWinDCAction(workflows.Action): dc_name = forms.CharField(label=_("Domain Name"), required=False, @@ -129,11 +143,11 @@ class ConfigureWinIIS(workflows.Step): class CreateWinService(workflows.Workflow): slug = "create" - name = _("Create Windows Service") + name = _("Create Service") finalize_button_name = _("Deploy") success_message = _('Deployed %(count)s named "%(name)s".') failure_message = _('Unable to deploy %(count)s named "%(name)s".') - success_url = "horizon:project:windc:index" + success_url = "horizon:project:windc:services" default_steps = (SelectProjectUser, ConfigureWinDC, ConfigureWinIIS) @@ -148,3 +162,25 @@ class CreateWinService(workflows.Workflow): # except: # exceptions.handle(request) # return False + + +class CreateWinDC(workflows.Workflow): + slug = "create" + name = _("Create Windows Data Center") + finalize_button_name = _("Deploy") + success_message = _('Deployed %(count)s named "%(name)s".') + failure_message = _('Unable to deploy %(count)s named "%(name)s".') + success_url = "horizon:project:windc" + default_steps = (SelectProjectUser, + ConfigureDC) + + ## TO DO: + ## Need to rewrite the following code: + + #def handle(self, request, context): + # try: + # api.windc.create(request,...) + # return True + # except: + # exceptions.handle(request) + # return False