From b3813f31d09287e83299d9bd8bb43e958e656800 Mon Sep 17 00:00:00 2001 From: Haejin Choe Date: Wed, 14 Feb 2024 10:19:50 +0000 Subject: [PATCH] Support NFV Orchestration API v2.0 Support the latest NFV Orchestration API v2.0 listed below. * VNF LCM v2 * VNF FM v1 * VNF PM v2 In addition, to manage VNF packages for testing purposes, also support Orchestration API v1.0 listed below. * VNF packages Implements: blueprint v2-api-horizon Change-Id: I0284cba30154cf4cc5d496be157288e2c2614ccf --- ...orchestration-api-v2-a0ec6da7c67e6d4f.yaml | 13 + .../openstack_dashboard/api/tacker.py | 370 ++++++++++++++- .../dashboards/nfv/dashboard.py | 31 +- .../nfv/lccnsubscription/__init__.py | 0 .../dashboards/nfv/lccnsubscription/forms.py | 65 +++ .../dashboards/nfv/lccnsubscription/panel.py | 29 ++ .../dashboards/nfv/lccnsubscription/tables.py | 73 +++ .../dashboards/nfv/lccnsubscription/tabs.py | 109 +++++ .../_create_lccn_subscription.html | 9 + .../create_lccn_subscription.html | 11 + .../templates/lccnsubscription/detail.html | 15 + .../templates/lccnsubscription/index.html | 15 + .../lccnsubscription/lccnsubsc_detail.html | 14 + .../dashboards/nfv/lccnsubscription/urls.py | 29 ++ .../dashboards/nfv/lccnsubscription/views.py | 88 ++++ .../dashboards/nfv/vnffmalarm/__init__.py | 0 .../dashboards/nfv/vnffmalarm/forms.py | 52 +++ .../dashboards/nfv/vnffmalarm/panel.py | 29 ++ .../dashboards/nfv/vnffmalarm/tables.py | 52 +++ .../dashboards/nfv/vnffmalarm/tabs.py | 114 +++++ .../templates/vnffmalarm/_update_alarm.html | 9 + .../templates/vnffmalarm/alarm_detail.html | 42 ++ .../templates/vnffmalarm/detail.html | 15 + .../templates/vnffmalarm/index.html | 15 + .../templates/vnffmalarm/update_alarm.html | 11 + .../dashboards/nfv/vnffmalarm/urls.py | 29 ++ .../dashboards/nfv/vnffmalarm/views.py | 90 ++++ .../nfv/vnffmsubscription/__init__.py | 0 .../dashboards/nfv/vnffmsubscription/forms.py | 64 +++ .../dashboards/nfv/vnffmsubscription/panel.py | 29 ++ .../nfv/vnffmsubscription/tables.py | 72 +++ .../dashboards/nfv/vnffmsubscription/tabs.py | 106 +++++ .../_create_subscription.html | 9 + .../create_subscription.html | 11 + .../templates/vnffmsubscription/detail.html | 15 + .../templates/vnffmsubscription/index.html | 15 + .../subscription_detail.html | 14 + .../dashboards/nfv/vnffmsubscription/urls.py | 29 ++ .../dashboards/nfv/vnffmsubscription/views.py | 94 ++++ .../dashboards/nfv/vnflcm/__init__.py | 0 .../dashboards/nfv/vnflcm/forms.py | 425 ++++++++++++++++++ .../dashboards/nfv/vnflcm/panel.py | 29 ++ .../dashboards/nfv/vnflcm/tables.py | 133 ++++++ .../dashboards/nfv/vnflcm/tabs.py | 131 ++++++ .../vnflcm/_change_connectivity.html | 9 + .../templates/vnflcm/_change_vnfpkg.html | 9 + .../vnflcm/_create_vnf_identifier.html | 9 + .../vnflcm/templates/vnflcm/_heal_vnf.html | 9 + .../templates/vnflcm/_instantiate_vnf.html | 9 + .../vnflcm/templates/vnflcm/_scale_vnf.html | 9 + .../templates/vnflcm/_terminate_vnf.html | 9 + .../vnflcm/templates/vnflcm/_update_vnf.html | 9 + .../templates/vnflcm/change_connectivity.html | 11 + .../templates/vnflcm/change_vnfpkg.html | 11 + .../vnflcm/create_vnf_identifier.html | 11 + .../nfv/vnflcm/templates/vnflcm/detail.html | 15 + .../nfv/vnflcm/templates/vnflcm/heal_vnf.html | 11 + .../nfv/vnflcm/templates/vnflcm/index.html | 15 + .../templates/vnflcm/instantiate_vnf.html | 11 + .../vnflcm/templates/vnflcm/scale_vnf.html | 11 + .../templates/vnflcm/terminate_vnf.html | 11 + .../vnflcm/templates/vnflcm/update_vnf.html | 11 + .../templates/vnflcm/vnflcm_detail.html | 30 ++ .../dashboards/nfv/vnflcm/urls.py | 44 ++ .../dashboards/nfv/vnflcm/views.py | 200 +++++++++ .../dashboards/nfv/vnflcmopocc/__init__.py | 0 .../dashboards/nfv/vnflcmopocc/panel.py | 29 ++ .../dashboards/nfv/vnflcmopocc/tables.py | 118 +++++ .../dashboards/nfv/vnflcmopocc/tabs.py | 118 +++++ .../templates/vnflcmopocc/detail.html | 15 + .../templates/vnflcmopocc/index.html | 15 + .../vnflcmopocc/vnflcmopocc_detail.html | 38 ++ .../dashboards/nfv/vnflcmopocc/urls.py | 25 ++ .../dashboards/nfv/vnflcmopocc/views.py | 73 +++ .../dashboards/nfv/vnfpackages/__init__.py | 0 .../dashboards/nfv/vnfpackages/forms.py | 189 ++++++++ .../dashboards/nfv/vnfpackages/panel.py | 29 ++ .../dashboards/nfv/vnfpackages/tables.py | 119 +++++ .../dashboards/nfv/vnfpackages/tabs.py | 118 +++++ .../templates/vnfpackages/_update_vnfpkg.html | 9 + .../templates/vnfpackages/_upload_vnfpkg.html | 9 + .../templates/vnfpackages/detail.html | 15 + .../templates/vnfpackages/index.html | 15 + .../templates/vnfpackages/update_vnfpkg.html | 11 + .../templates/vnfpackages/upload_vnfpkg.html | 11 + .../templates/vnfpackages/vnfpkg_detail.html | 34 ++ .../dashboards/nfv/vnfpackages/urls.py | 31 ++ .../dashboards/nfv/vnfpackages/views.py | 121 +++++ .../dashboards/nfv/vnfpmjob/__init__.py | 0 .../dashboards/nfv/vnfpmjob/forms.py | 102 +++++ .../dashboards/nfv/vnfpmjob/panel.py | 29 ++ .../dashboards/nfv/vnfpmjob/tables.py | 78 ++++ .../dashboards/nfv/vnfpmjob/tabs.py | 122 +++++ .../templates/vnfpmjob/_create_pmjob.html | 9 + .../templates/vnfpmjob/_update_pmjob.html | 9 + .../templates/vnfpmjob/create_pmjob.html | 11 + .../vnfpmjob/templates/vnfpmjob/detail.html | 15 + .../vnfpmjob/templates/vnfpmjob/index.html | 15 + .../templates/vnfpmjob/pmjob_detail.html | 57 +++ .../templates/vnfpmjob/report_detail.html | 39 ++ .../templates/vnfpmjob/update_pmjob.html | 11 + .../dashboards/nfv/vnfpmjob/urls.py | 33 ++ .../dashboards/nfv/vnfpmjob/views.py | 172 +++++++ .../dashboards/nfv/vnfpmthreshold/__init__.py | 0 .../dashboards/nfv/vnfpmthreshold/forms.py | 102 +++++ .../dashboards/nfv/vnfpmthreshold/panel.py | 29 ++ .../dashboards/nfv/vnfpmthreshold/tables.py | 78 ++++ .../dashboards/nfv/vnfpmthreshold/tabs.py | 108 +++++ .../vnfpmthreshold/_create_pmthreshold.html | 9 + .../vnfpmthreshold/_update_pmthreshold.html | 9 + .../vnfpmthreshold/create_pmthreshold.html | 11 + .../templates/vnfpmthreshold/detail.html | 15 + .../templates/vnfpmthreshold/index.html | 15 + .../vnfpmthreshold/pmthreshold_detail.html | 20 + .../vnfpmthreshold/update_pmthreshold.html | 11 + .../dashboards/nfv/vnfpmthreshold/urls.py | 31 ++ .../dashboards/nfv/vnfpmthreshold/views.py | 114 +++++ tacker_horizon/test/integration/horizon.conf | 2 +- .../integration/pages/nfv/vnffm/__init__.py | 0 .../pages/nfv/vnffm/vnffmalarmpage.py | 22 + .../pages/nfv/vnffm/vnffmsubscriptionpage.py | 22 + .../integration/pages/nfv/vnflcm/__init__.py | 0 .../pages/nfv/vnflcm/lccnsubscriptionpage.py | 22 + .../pages/nfv/vnflcm/vnflcmopoccpage.py | 22 + .../pages/nfv/vnflcm/vnflcmpage.py | 22 + .../pages/nfv/vnfpackages/__init__.py | 0 .../pages/nfv/vnfpackages/vnfpackagespage.py | 22 + .../integration/pages/nfv/vnfpm/__init__.py | 0 .../pages/nfv/vnfpm/vnfpmjobpage.py | 22 + .../pages/nfv/vnfpm/vnfpmthresholdpage.py | 22 + tacker_horizon/test/integration/test_basic.py | 40 ++ 131 files changed, 5578 insertions(+), 5 deletions(-) create mode 100644 releasenotes/notes/support-nfv-orchestration-api-v2-a0ec6da7c67e6d4f.yaml create mode 100644 tacker_horizon/openstack_dashboard/dashboards/nfv/lccnsubscription/__init__.py create mode 100644 tacker_horizon/openstack_dashboard/dashboards/nfv/lccnsubscription/forms.py create mode 100644 tacker_horizon/openstack_dashboard/dashboards/nfv/lccnsubscription/panel.py create mode 100644 tacker_horizon/openstack_dashboard/dashboards/nfv/lccnsubscription/tables.py create mode 100644 tacker_horizon/openstack_dashboard/dashboards/nfv/lccnsubscription/tabs.py create mode 100644 tacker_horizon/openstack_dashboard/dashboards/nfv/lccnsubscription/templates/lccnsubscription/_create_lccn_subscription.html create mode 100644 tacker_horizon/openstack_dashboard/dashboards/nfv/lccnsubscription/templates/lccnsubscription/create_lccn_subscription.html create mode 100644 tacker_horizon/openstack_dashboard/dashboards/nfv/lccnsubscription/templates/lccnsubscription/detail.html create mode 100644 tacker_horizon/openstack_dashboard/dashboards/nfv/lccnsubscription/templates/lccnsubscription/index.html create mode 100644 tacker_horizon/openstack_dashboard/dashboards/nfv/lccnsubscription/templates/lccnsubscription/lccnsubsc_detail.html create mode 100644 tacker_horizon/openstack_dashboard/dashboards/nfv/lccnsubscription/urls.py create mode 100644 tacker_horizon/openstack_dashboard/dashboards/nfv/lccnsubscription/views.py create mode 100644 tacker_horizon/openstack_dashboard/dashboards/nfv/vnffmalarm/__init__.py create mode 100644 tacker_horizon/openstack_dashboard/dashboards/nfv/vnffmalarm/forms.py create mode 100644 tacker_horizon/openstack_dashboard/dashboards/nfv/vnffmalarm/panel.py create mode 100644 tacker_horizon/openstack_dashboard/dashboards/nfv/vnffmalarm/tables.py create mode 100644 tacker_horizon/openstack_dashboard/dashboards/nfv/vnffmalarm/tabs.py create mode 100644 tacker_horizon/openstack_dashboard/dashboards/nfv/vnffmalarm/templates/vnffmalarm/_update_alarm.html create mode 100644 tacker_horizon/openstack_dashboard/dashboards/nfv/vnffmalarm/templates/vnffmalarm/alarm_detail.html create mode 100644 tacker_horizon/openstack_dashboard/dashboards/nfv/vnffmalarm/templates/vnffmalarm/detail.html create mode 100644 tacker_horizon/openstack_dashboard/dashboards/nfv/vnffmalarm/templates/vnffmalarm/index.html create mode 100644 tacker_horizon/openstack_dashboard/dashboards/nfv/vnffmalarm/templates/vnffmalarm/update_alarm.html create mode 100644 tacker_horizon/openstack_dashboard/dashboards/nfv/vnffmalarm/urls.py create mode 100644 tacker_horizon/openstack_dashboard/dashboards/nfv/vnffmalarm/views.py create mode 100644 tacker_horizon/openstack_dashboard/dashboards/nfv/vnffmsubscription/__init__.py create mode 100644 tacker_horizon/openstack_dashboard/dashboards/nfv/vnffmsubscription/forms.py create mode 100644 tacker_horizon/openstack_dashboard/dashboards/nfv/vnffmsubscription/panel.py create mode 100644 tacker_horizon/openstack_dashboard/dashboards/nfv/vnffmsubscription/tables.py create mode 100644 tacker_horizon/openstack_dashboard/dashboards/nfv/vnffmsubscription/tabs.py create mode 100644 tacker_horizon/openstack_dashboard/dashboards/nfv/vnffmsubscription/templates/vnffmsubscription/_create_subscription.html create mode 100644 tacker_horizon/openstack_dashboard/dashboards/nfv/vnffmsubscription/templates/vnffmsubscription/create_subscription.html create mode 100644 tacker_horizon/openstack_dashboard/dashboards/nfv/vnffmsubscription/templates/vnffmsubscription/detail.html create mode 100644 tacker_horizon/openstack_dashboard/dashboards/nfv/vnffmsubscription/templates/vnffmsubscription/index.html create mode 100644 tacker_horizon/openstack_dashboard/dashboards/nfv/vnffmsubscription/templates/vnffmsubscription/subscription_detail.html create mode 100644 tacker_horizon/openstack_dashboard/dashboards/nfv/vnffmsubscription/urls.py create mode 100644 tacker_horizon/openstack_dashboard/dashboards/nfv/vnffmsubscription/views.py create mode 100644 tacker_horizon/openstack_dashboard/dashboards/nfv/vnflcm/__init__.py create mode 100644 tacker_horizon/openstack_dashboard/dashboards/nfv/vnflcm/forms.py create mode 100644 tacker_horizon/openstack_dashboard/dashboards/nfv/vnflcm/panel.py create mode 100644 tacker_horizon/openstack_dashboard/dashboards/nfv/vnflcm/tables.py create mode 100644 tacker_horizon/openstack_dashboard/dashboards/nfv/vnflcm/tabs.py create mode 100644 tacker_horizon/openstack_dashboard/dashboards/nfv/vnflcm/templates/vnflcm/_change_connectivity.html create mode 100644 tacker_horizon/openstack_dashboard/dashboards/nfv/vnflcm/templates/vnflcm/_change_vnfpkg.html create mode 100644 tacker_horizon/openstack_dashboard/dashboards/nfv/vnflcm/templates/vnflcm/_create_vnf_identifier.html create mode 100644 tacker_horizon/openstack_dashboard/dashboards/nfv/vnflcm/templates/vnflcm/_heal_vnf.html create mode 100644 tacker_horizon/openstack_dashboard/dashboards/nfv/vnflcm/templates/vnflcm/_instantiate_vnf.html create mode 100644 tacker_horizon/openstack_dashboard/dashboards/nfv/vnflcm/templates/vnflcm/_scale_vnf.html create mode 100644 tacker_horizon/openstack_dashboard/dashboards/nfv/vnflcm/templates/vnflcm/_terminate_vnf.html create mode 100644 tacker_horizon/openstack_dashboard/dashboards/nfv/vnflcm/templates/vnflcm/_update_vnf.html create mode 100644 tacker_horizon/openstack_dashboard/dashboards/nfv/vnflcm/templates/vnflcm/change_connectivity.html create mode 100644 tacker_horizon/openstack_dashboard/dashboards/nfv/vnflcm/templates/vnflcm/change_vnfpkg.html create mode 100644 tacker_horizon/openstack_dashboard/dashboards/nfv/vnflcm/templates/vnflcm/create_vnf_identifier.html create mode 100644 tacker_horizon/openstack_dashboard/dashboards/nfv/vnflcm/templates/vnflcm/detail.html create mode 100644 tacker_horizon/openstack_dashboard/dashboards/nfv/vnflcm/templates/vnflcm/heal_vnf.html create mode 100644 tacker_horizon/openstack_dashboard/dashboards/nfv/vnflcm/templates/vnflcm/index.html create mode 100644 tacker_horizon/openstack_dashboard/dashboards/nfv/vnflcm/templates/vnflcm/instantiate_vnf.html create mode 100644 tacker_horizon/openstack_dashboard/dashboards/nfv/vnflcm/templates/vnflcm/scale_vnf.html create mode 100644 tacker_horizon/openstack_dashboard/dashboards/nfv/vnflcm/templates/vnflcm/terminate_vnf.html create mode 100644 tacker_horizon/openstack_dashboard/dashboards/nfv/vnflcm/templates/vnflcm/update_vnf.html create mode 100644 tacker_horizon/openstack_dashboard/dashboards/nfv/vnflcm/templates/vnflcm/vnflcm_detail.html create mode 100644 tacker_horizon/openstack_dashboard/dashboards/nfv/vnflcm/urls.py create mode 100644 tacker_horizon/openstack_dashboard/dashboards/nfv/vnflcm/views.py create mode 100644 tacker_horizon/openstack_dashboard/dashboards/nfv/vnflcmopocc/__init__.py create mode 100644 tacker_horizon/openstack_dashboard/dashboards/nfv/vnflcmopocc/panel.py create mode 100644 tacker_horizon/openstack_dashboard/dashboards/nfv/vnflcmopocc/tables.py create mode 100644 tacker_horizon/openstack_dashboard/dashboards/nfv/vnflcmopocc/tabs.py create mode 100644 tacker_horizon/openstack_dashboard/dashboards/nfv/vnflcmopocc/templates/vnflcmopocc/detail.html create mode 100644 tacker_horizon/openstack_dashboard/dashboards/nfv/vnflcmopocc/templates/vnflcmopocc/index.html create mode 100644 tacker_horizon/openstack_dashboard/dashboards/nfv/vnflcmopocc/templates/vnflcmopocc/vnflcmopocc_detail.html create mode 100644 tacker_horizon/openstack_dashboard/dashboards/nfv/vnflcmopocc/urls.py create mode 100644 tacker_horizon/openstack_dashboard/dashboards/nfv/vnflcmopocc/views.py create mode 100644 tacker_horizon/openstack_dashboard/dashboards/nfv/vnfpackages/__init__.py create mode 100644 tacker_horizon/openstack_dashboard/dashboards/nfv/vnfpackages/forms.py create mode 100644 tacker_horizon/openstack_dashboard/dashboards/nfv/vnfpackages/panel.py create mode 100644 tacker_horizon/openstack_dashboard/dashboards/nfv/vnfpackages/tables.py create mode 100644 tacker_horizon/openstack_dashboard/dashboards/nfv/vnfpackages/tabs.py create mode 100644 tacker_horizon/openstack_dashboard/dashboards/nfv/vnfpackages/templates/vnfpackages/_update_vnfpkg.html create mode 100644 tacker_horizon/openstack_dashboard/dashboards/nfv/vnfpackages/templates/vnfpackages/_upload_vnfpkg.html create mode 100644 tacker_horizon/openstack_dashboard/dashboards/nfv/vnfpackages/templates/vnfpackages/detail.html create mode 100644 tacker_horizon/openstack_dashboard/dashboards/nfv/vnfpackages/templates/vnfpackages/index.html create mode 100644 tacker_horizon/openstack_dashboard/dashboards/nfv/vnfpackages/templates/vnfpackages/update_vnfpkg.html create mode 100644 tacker_horizon/openstack_dashboard/dashboards/nfv/vnfpackages/templates/vnfpackages/upload_vnfpkg.html create mode 100644 tacker_horizon/openstack_dashboard/dashboards/nfv/vnfpackages/templates/vnfpackages/vnfpkg_detail.html create mode 100644 tacker_horizon/openstack_dashboard/dashboards/nfv/vnfpackages/urls.py create mode 100644 tacker_horizon/openstack_dashboard/dashboards/nfv/vnfpackages/views.py create mode 100644 tacker_horizon/openstack_dashboard/dashboards/nfv/vnfpmjob/__init__.py create mode 100644 tacker_horizon/openstack_dashboard/dashboards/nfv/vnfpmjob/forms.py create mode 100644 tacker_horizon/openstack_dashboard/dashboards/nfv/vnfpmjob/panel.py create mode 100644 tacker_horizon/openstack_dashboard/dashboards/nfv/vnfpmjob/tables.py create mode 100644 tacker_horizon/openstack_dashboard/dashboards/nfv/vnfpmjob/tabs.py create mode 100644 tacker_horizon/openstack_dashboard/dashboards/nfv/vnfpmjob/templates/vnfpmjob/_create_pmjob.html create mode 100644 tacker_horizon/openstack_dashboard/dashboards/nfv/vnfpmjob/templates/vnfpmjob/_update_pmjob.html create mode 100644 tacker_horizon/openstack_dashboard/dashboards/nfv/vnfpmjob/templates/vnfpmjob/create_pmjob.html create mode 100644 tacker_horizon/openstack_dashboard/dashboards/nfv/vnfpmjob/templates/vnfpmjob/detail.html create mode 100644 tacker_horizon/openstack_dashboard/dashboards/nfv/vnfpmjob/templates/vnfpmjob/index.html create mode 100644 tacker_horizon/openstack_dashboard/dashboards/nfv/vnfpmjob/templates/vnfpmjob/pmjob_detail.html create mode 100644 tacker_horizon/openstack_dashboard/dashboards/nfv/vnfpmjob/templates/vnfpmjob/report_detail.html create mode 100644 tacker_horizon/openstack_dashboard/dashboards/nfv/vnfpmjob/templates/vnfpmjob/update_pmjob.html create mode 100644 tacker_horizon/openstack_dashboard/dashboards/nfv/vnfpmjob/urls.py create mode 100644 tacker_horizon/openstack_dashboard/dashboards/nfv/vnfpmjob/views.py create mode 100644 tacker_horizon/openstack_dashboard/dashboards/nfv/vnfpmthreshold/__init__.py create mode 100644 tacker_horizon/openstack_dashboard/dashboards/nfv/vnfpmthreshold/forms.py create mode 100644 tacker_horizon/openstack_dashboard/dashboards/nfv/vnfpmthreshold/panel.py create mode 100644 tacker_horizon/openstack_dashboard/dashboards/nfv/vnfpmthreshold/tables.py create mode 100644 tacker_horizon/openstack_dashboard/dashboards/nfv/vnfpmthreshold/tabs.py create mode 100644 tacker_horizon/openstack_dashboard/dashboards/nfv/vnfpmthreshold/templates/vnfpmthreshold/_create_pmthreshold.html create mode 100644 tacker_horizon/openstack_dashboard/dashboards/nfv/vnfpmthreshold/templates/vnfpmthreshold/_update_pmthreshold.html create mode 100644 tacker_horizon/openstack_dashboard/dashboards/nfv/vnfpmthreshold/templates/vnfpmthreshold/create_pmthreshold.html create mode 100644 tacker_horizon/openstack_dashboard/dashboards/nfv/vnfpmthreshold/templates/vnfpmthreshold/detail.html create mode 100644 tacker_horizon/openstack_dashboard/dashboards/nfv/vnfpmthreshold/templates/vnfpmthreshold/index.html create mode 100644 tacker_horizon/openstack_dashboard/dashboards/nfv/vnfpmthreshold/templates/vnfpmthreshold/pmthreshold_detail.html create mode 100644 tacker_horizon/openstack_dashboard/dashboards/nfv/vnfpmthreshold/templates/vnfpmthreshold/update_pmthreshold.html create mode 100644 tacker_horizon/openstack_dashboard/dashboards/nfv/vnfpmthreshold/urls.py create mode 100644 tacker_horizon/openstack_dashboard/dashboards/nfv/vnfpmthreshold/views.py create mode 100644 tacker_horizon/test/integration/pages/nfv/vnffm/__init__.py create mode 100644 tacker_horizon/test/integration/pages/nfv/vnffm/vnffmalarmpage.py create mode 100644 tacker_horizon/test/integration/pages/nfv/vnffm/vnffmsubscriptionpage.py create mode 100644 tacker_horizon/test/integration/pages/nfv/vnflcm/__init__.py create mode 100644 tacker_horizon/test/integration/pages/nfv/vnflcm/lccnsubscriptionpage.py create mode 100644 tacker_horizon/test/integration/pages/nfv/vnflcm/vnflcmopoccpage.py create mode 100644 tacker_horizon/test/integration/pages/nfv/vnflcm/vnflcmpage.py create mode 100644 tacker_horizon/test/integration/pages/nfv/vnfpackages/__init__.py create mode 100644 tacker_horizon/test/integration/pages/nfv/vnfpackages/vnfpackagespage.py create mode 100644 tacker_horizon/test/integration/pages/nfv/vnfpm/__init__.py create mode 100644 tacker_horizon/test/integration/pages/nfv/vnfpm/vnfpmjobpage.py create mode 100644 tacker_horizon/test/integration/pages/nfv/vnfpm/vnfpmthresholdpage.py diff --git a/releasenotes/notes/support-nfv-orchestration-api-v2-a0ec6da7c67e6d4f.yaml b/releasenotes/notes/support-nfv-orchestration-api-v2-a0ec6da7c67e6d4f.yaml new file mode 100644 index 0000000..3eecf3b --- /dev/null +++ b/releasenotes/notes/support-nfv-orchestration-api-v2-a0ec6da7c67e6d4f.yaml @@ -0,0 +1,13 @@ +--- +features: + - | + Support the latest NFV Orchestration API v2.0 listed below. + + * Virtualized Network Function Lifecycle Management Interface (VNF LCM) v2 + * Virtualized Network Function Fault Management Interface (VNF FM) v1 + * Virtualized Network Function Performance Management Interface (VNF PM) v2 + + In addition, to manage VNF packages for testing purposes, also support + Orchestration API v1.0 listed below. + + * Virtualized Network Function Packages (VNF packages) diff --git a/tacker_horizon/openstack_dashboard/api/tacker.py b/tacker_horizon/openstack_dashboard/api/tacker.py index 18910c5..e6c0408 100644 --- a/tacker_horizon/openstack_dashboard/api/tacker.py +++ b/tacker_horizon/openstack_dashboard/api/tacker.py @@ -44,10 +44,11 @@ CERT_VERIFY_TYPES = ( @memoized -def tackerclient(request): +def tackerclient(request, api_version="1"): insecure = getattr(settings, 'OPENSTACK_SSL_NO_VERIFY', False) cacert = getattr(settings, 'OPENSTACK_SSL_CACERT', None) c = tacker_client.Client( + api_version=api_version, token=request.user.token.id, auth_url=base.url_for(request, 'identity'), endpoint_url=base.url_for(request, 'nfv-orchestration'), @@ -251,3 +252,370 @@ def create_ns(request, ns_arg, **params): LOG.debug("create_ns(): ns_arg=%s", str(ns_arg)) ns_instance = tackerclient(request).create_ns(body=ns_arg) return ns_instance + + +# VNF Packages v1 +@profiler.trace +def create_vnf_package(request, body): + LOG.debug("create_vnf_package(): body=%s", body) + vnf_package = tackerclient(request).create_vnf_package(body=body) + return vnf_package + + +@profiler.trace +def list_vnf_packages(request, **params): + LOG.debug("list_vnf_packages(): params=%s", params) + vnf_packages = tackerclient(request).list_vnf_packages(**params).get( + 'vnf_packages') + return vnf_packages + + +@profiler.trace +def get_vnf_package(request, vnf_pkg_id): + LOG.debug("get_vnf_package(): vnf_pkg_id=%s", vnf_pkg_id) + vnf_package = tackerclient(request).show_vnf_package(vnf_pkg_id) + return vnf_package + + +@profiler.trace +def delete_vnf_package(request, vnf_pkg_id): + LOG.debug("delete_vnf_package(): vnf_pkg_id=%s", vnf_pkg_id) + result = tackerclient(request).delete_vnf_package(vnf_pkg_id) + return result + + +@profiler.trace +def upload_vnf_package(request, vnf_pkg_id, file_data=None, **params): + LOG.debug("upload_vnf_package(): vnf_pkg_id=%s, params=%s", + vnf_pkg_id, params) + vnf_package = tackerclient(request).upload_vnf_package( + vnf_pkg_id, file_data=file_data, **params) + return vnf_package + + +@profiler.trace +def update_vnf_package(request, vnf_pkg_id, body): + LOG.debug("update_vnf_package(): vnf_pkg_id=%s, body=%s", + vnf_pkg_id, body) + updated_values = tackerclient(request).update_vnf_package( + vnf_pkg_id, body=body) + return updated_values + + +@profiler.trace +def fetch_vnf_package(request, vnf_pkg_id): + LOG.debug("fetch_vnf_package(): vnf_pkg_id=%s", vnf_pkg_id) + vnf_package_file = tackerclient(request).download_vnf_package(vnf_pkg_id) + return vnf_package_file + + +# VNF LCM v2 +@profiler.trace +def create_vnf_instance(request, body): + LOG.debug("create_vnf_instance(): body=%s", body) + vnf_instance = (tackerclient(request, api_version="2") + .create_vnf_instance(body=body)) + return vnf_instance + + +@profiler.trace +def get_vnf_instance(request, vnf_instance_id): + LOG.debug("get_vnf_instance(): vnf_instance_id=%s", vnf_instance_id) + vnf_instance = (tackerclient(request, api_version="2") + .show_vnf_instance(vnf_instance_id)) + return vnf_instance + + +@profiler.trace +def list_vnf_instances(request, **params): + LOG.debug("list_vnf_instances(): params=%s", params) + vnf_instances = (tackerclient(request, api_version="2") + .list_vnf_instances(**params)) + return vnf_instances + + +@profiler.trace +def delete_vnf_instance(request, vnf_instance_id): + LOG.debug("delete_vnf_instance(): vnf_instance_id=%s", vnf_instance_id) + result = (tackerclient(request, api_version="2") + .delete_vnf_instance(vnf_instance_id)) + return result + + +@profiler.trace +def instantiate_vnf_instance(request, vnf_instance_id, body): + LOG.debug("instantiate_vnf_instance(): vnf_instance_id=%s, body=%s", + vnf_instance_id, body) + result = (tackerclient(request, api_version="2") + .instantiate_vnf_instance(vnf_instance_id, body=body)) + return result + + +@profiler.trace +def terminate_vnf_instance(request, vnf_instance_id, body): + LOG.debug("terminate_vnf_instance(): vnf_instance_id=%s, body=%s", + vnf_instance_id, body) + result = (tackerclient(request, api_version="2") + .terminate_vnf_instance(vnf_instance_id, body=body)) + return result + + +@profiler.trace +def heal_vnf_instance(request, vnf_instance_id, body): + LOG.debug("heal_vnf_instance(): vnf_instance_id=%s, body=%s", + vnf_instance_id, body) + result = (tackerclient(request, api_version="2") + .heal_vnf_instance(vnf_instance_id, body=body)) + return result + + +@profiler.trace +def update_vnf_instance(request, vnf_instance_id, body): + LOG.debug("update_vnf_instance(): vnf_instance_id=%s, body=%s", + vnf_instance_id, body) + result = (tackerclient(request, api_version="2") + .update_vnf_instance(vnf_instance_id, body=body)) + return result + + +@profiler.trace +def scale_vnf_instance(request, vnf_instance_id, body): + LOG.debug("scale_vnf_instance(): vnf_instance_id=%s, body=%s", + vnf_instance_id, body) + result = (tackerclient(request, api_version="2") + .scale_vnf_instance(vnf_instance_id, body=body)) + return result + + +@profiler.trace +def change_ext_conn_vnf_instance(request, vnf_instance_id, body): + LOG.debug("change_ext_conn_vnf_instance(): vnf_instance_id=%s, body=%s", + vnf_instance_id, body) + result = (tackerclient(request, api_version="2") + .change_ext_conn_vnf_instance(vnf_instance_id, body=body)) + return result + + +@profiler.trace +def change_vnfpkg_vnf_instance(request, vnf_instance_id, body): + LOG.debug("change_vnfpkg_vnf_instance(): vnf_instance_id=%s, body=%s", + vnf_instance_id, body) + result = (tackerclient(request, api_version="2") + .change_vnfpkg_vnf_instance(vnf_instance_id, body=body)) + return result + + +@profiler.trace +def list_vnf_lcm_op_occs(request, **params): + LOG.debug("list_vnf_lcm_op_occs(): params=%s", params) + op_occs = (tackerclient(request, api_version="2") + .list_vnf_lcm_op_occs(**params)) + return op_occs + + +@profiler.trace +def get_vnf_lcm_op_occ(request, op_occ_id): + LOG.debug("get_vnf_lcm_op_occ(): op_occ_id=%s", op_occ_id) + op_occ = (tackerclient(request, api_version="2") + .show_vnf_lcm_op_occs(op_occ_id)) + return op_occ + + +@profiler.trace +def rollback_vnf_lcm_op_occ(request, op_occ_id): + LOG.debug("rollback_vnf_lcm_op_occ(): op_occ_id=%s", op_occ_id) + result = (tackerclient(request, api_version="2") + .rollback_vnf_instance(op_occ_id)) + return result + + +@profiler.trace +def retry_vnf_lcm_op_occ(request, op_occ_id): + LOG.debug("retry_vnf_lcm_op_occ(): op_occ_id=%s", op_occ_id) + result = (tackerclient(request, api_version="2") + .retry_vnf_instance(op_occ_id)) + return result + + +@profiler.trace +def fail_vnf_lcm_op_occ(request, op_occ_id): + LOG.debug("fail_vnf_lcm_op_occs(): op_occ_id=%s", op_occ_id) + result = (tackerclient(request, api_version="2") + .fail_vnf_instance(op_occ_id)) + return result + + +@profiler.trace +def list_vnf_lcm_subscriptions(request, **params): + LOG.debug("list_vnf_lcm_subscriptions(): params=%s", params) + subscriptions = (tackerclient(request, api_version="2") + .list_lccn_subscriptions(**params)) + return subscriptions + + +@profiler.trace +def get_vnf_lcm_subscription(request, subsc_id): + LOG.debug("get_vnf_lcm_subscription(): subsc_id=%s", subsc_id) + subscription = (tackerclient(request, api_version="2") + .show_lccn_subscription(subsc_id)) + return subscription + + +@profiler.trace +def delete_vnf_lcm_subscription(request, subsc_id): + LOG.debug("delete_vnf_lcm_subscription(): subsc_id=%s", subsc_id) + result = (tackerclient(request, api_version="2") + .delete_lccn_subscription(subsc_id)) + return result + + +@profiler.trace +def create_vnf_lcm_subscription(request, param): + LOG.debug("create_vnf_lcm_subscription(): param=%s", param) + subscription = (tackerclient(request, api_version="2") + .create_lccn_subscription(body=param)) + return subscription + + +# VNF FM v1 +@profiler.trace +def list_fm_alarms(request, **params): + LOG.debug("list_fm_alarms(): params=%s", params) + fm_alarms = tackerclient(request).list_vnf_fm_alarms(**params).get( + 'vnf_fm_alarms') + return fm_alarms + + +@profiler.trace +def get_fm_alarm(request, alarm_id): + LOG.debug("get_fm_alarm(): alarm_id=%s", alarm_id) + fm_alarm = tackerclient(request).show_vnf_fm_alarm(alarm_id) + return fm_alarm + + +@profiler.trace +def update_fm_alarm(request, alarm_id, body): + LOG.debug("update_fm_alarm(): alarm_id=%s, body=%s", alarm_id, body) + updated_values = tackerclient(request).update_vnf_fm_alarm( + alarm_id, body=body) + return updated_values + + +@profiler.trace +def create_fm_subscription(request, body): + LOG.debug("create_fm_subscription(): body=%s", body) + fm_subscription = tackerclient(request).create_vnf_fm_sub(body=body) + return fm_subscription + + +@profiler.trace +def list_fm_subscriptions(request, **params): + LOG.debug("list_fm_subscriptions(): params=%s", params) + fm_subscriptions = tackerclient(request).list_vnf_fm_subs(**params).get( + 'vnf_fm_subs') + return fm_subscriptions + + +@profiler.trace +def get_fm_subscription(request, subsc_id): + LOG.debug("get_fm_subscription(): subsc_id=%s", subsc_id) + fm_subscription = tackerclient(request).show_vnf_fm_sub(subsc_id) + return fm_subscription + + +@profiler.trace +def delete_fm_subscription(request, subsc_id): + LOG.debug("delete_fm_subscription(): subsc_id=%s", subsc_id) + result = tackerclient(request).delete_vnf_fm_sub(subsc_id) + return result + + +# VNF PM v2 +@profiler.trace +def create_pm_job(request, body): + LOG.debug("create_pm_job(): body=%s", body) + pm_job = (tackerclient(request, api_version="2") + .create_vnf_pm_job(body=body)) + return pm_job + + +@profiler.trace +def list_pm_jobs(request, **params): + LOG.debug("list_pm_jobs(): params=%s", params) + pm_jobs = (tackerclient(request, api_version="2") + .list_vnf_pm_jobs(**params).get('vnf_pm_jobs')) + return pm_jobs + + +@profiler.trace +def get_pm_job(request, pm_job_id): + LOG.debug("get_pm_job(): pm_job_id=%s", pm_job_id) + pm_job = tackerclient(request, api_version="2").show_vnf_pm_job(pm_job_id) + return pm_job + + +@profiler.trace +def update_pm_job(request, pm_job_id, body): + LOG.debug("update_pm_job(): pm_job_id=%s, body=%s", pm_job_id, body) + updated_values = (tackerclient(request, api_version="2") + .update_vnf_pm_job(pm_job_id, body=body)) + return updated_values + + +@profiler.trace +def delete_pm_job(request, pm_job_id): + LOG.debug("delete_pm_job(): pm_job_id=%s", pm_job_id) + result = (tackerclient(request, api_version="2") + .delete_vnf_pm_job(pm_job_id)) + return result + + +@profiler.trace +def get_pm_report(request, pm_job_id, pm_report_id): + LOG.debug("get_pm_report(): pm_job_id=%s, pm_report_id=%s", + pm_job_id, pm_report_id) + pm_report = (tackerclient(request, api_version="2") + .show_vnf_pm_report(pm_job_id, pm_report_id)) + return pm_report + + +@profiler.trace +def create_pm_threshold(request, body): + LOG.debug("create_pm_threshold(): body=%s", body) + pm_threshold = (tackerclient(request, api_version="2") + .create_vnf_pm_threshold(body=body)) + return pm_threshold + + +@profiler.trace +def list_pm_thresholds(request, **params): + LOG.debug("list_pm_thresholds(): params=%s", params) + pm_thresholds = (tackerclient(request, api_version="2") + .list_vnf_pm_thresholds(**params) + .get('vnf_pm_thresholds')) + return pm_thresholds + + +@profiler.trace +def get_pm_threshold(request, pm_threshold_id): + LOG.debug("get_pm_threshold(): pm_threshold_id=%s", pm_threshold_id) + pm_threshold = (tackerclient(request, api_version="2") + .show_vnf_pm_threshold(pm_threshold_id)) + return pm_threshold + + +@profiler.trace +def update_pm_threshold(request, pm_threshold_id, body): + LOG.debug("update_pm_threshold(): pm_threshold_id=%s, body=%s", + pm_threshold_id, body) + updated_values = (tackerclient(request, api_version="2") + .update_vnf_pm_threshold(pm_threshold_id, body=body)) + return updated_values + + +@profiler.trace +def delete_pm_threshold(request, pm_threshold_id): + LOG.debug("delete_pm_threshold(): pm_threshold_id=%s", pm_threshold_id) + result = (tackerclient(request, api_version="2") + .delete_vnf_pm_threshold(pm_threshold_id)) + return result diff --git a/tacker_horizon/openstack_dashboard/dashboards/nfv/dashboard.py b/tacker_horizon/openstack_dashboard/dashboards/nfv/dashboard.py index 6b93802..f4b96d0 100644 --- a/tacker_horizon/openstack_dashboard/dashboards/nfv/dashboard.py +++ b/tacker_horizon/openstack_dashboard/dashboards/nfv/dashboard.py @@ -20,21 +20,46 @@ import horizon class Vnfmgroup(horizon.PanelGroup): slug = "nfvgroup" - name = _("VNF Management") + name = _("(Legacy)VNF Management") panels = ('vnfcatalog', 'vnfmanager',) class Nfvogroup(horizon.PanelGroup): slug = "nfvogroup" - name = _("NFV Orchestration") + name = _("(Legacy)NFV Orchestration") panels = ('vim', 'vnffgcatalog', 'vnffgmanager', 'nscatalog', 'nsmanager') +class Vnfpkggroup(horizon.PanelGroup): + slug = "vnfpkggroup" + name = _("VNF Packages v1") + panels = ('vnfpackages',) + + +class Vnflcmgroup(horizon.PanelGroup): + slug = "vnflcmgroup" + name = _("VNF LCM v2") + panels = ('vnflcm', 'vnflcmopocc', 'lccnsubscription',) + + +class Vnffmgroup(horizon.PanelGroup): + slug = "vnffmgroup" + name = _("VNF FM v1") + panels = ('vnffmalarm', 'vnffmsubscription',) + + +class Vnfpmgroup(horizon.PanelGroup): + slug = "vnfpmgroup" + name = _("VNF PM v2") + panels = ('vnfpmjob', 'vnfpmthreshold',) + + class Nfv(horizon.Dashboard): name = _("NFV") slug = "nfv" - panels = (Vnfmgroup, Nfvogroup,) # Add your panels here. + panels = (Vnfmgroup, Nfvogroup, Vnfpkggroup, Vnflcmgroup, Vnffmgroup, + Vnfpmgroup,) # Add your panels here. default_panel = 'vnfcatalog' # Specify the slug of the dashboard's # default panel. diff --git a/tacker_horizon/openstack_dashboard/dashboards/nfv/lccnsubscription/__init__.py b/tacker_horizon/openstack_dashboard/dashboards/nfv/lccnsubscription/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tacker_horizon/openstack_dashboard/dashboards/nfv/lccnsubscription/forms.py b/tacker_horizon/openstack_dashboard/dashboards/nfv/lccnsubscription/forms.py new file mode 100644 index 0000000..9e25fa6 --- /dev/null +++ b/tacker_horizon/openstack_dashboard/dashboards/nfv/lccnsubscription/forms.py @@ -0,0 +1,65 @@ +# Copyright (C) 2024 Fujitsu +# All Rights Reserved. +# +# 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. + +import json + +from django.utils.translation import gettext_lazy as _ + +from horizon import exceptions +from horizon import forms +from horizon import messages + +from tacker_horizon.openstack_dashboard import api + + +class CreateLccnSubscription(forms.SelfHandlingForm): + param_file = forms.FileField( + label=_("Param File"), + widget=forms.FileInput(attrs={ + 'class': 'switched', + 'data-switch-on': 'source', + 'data-source-upload': _('Param File')})) + + def __init__(self, request, *args, **kwargs): + super(CreateLccnSubscription, self).__init__(request, *args, **kwargs) + + def clean(self): + data = super(CreateLccnSubscription, self).clean() + + try: + param_str = self.files['param_file'].read() + param = json.loads(param_str) + data['param_data'] = param + except Exception as e: + msg = _('Failed to read file: %s.') % e + raise forms.ValidationError(msg) + + return data + + def handle(self, request, data): + try: + param = data['param_data'] + subsc = api.tacker.create_vnf_lcm_subscription(request, param) + messages.success( + request, + _('Create LCCN Subscription. (id: %s)') % subsc['id']) + + except Exception: + exceptions.handle( + request, + _('Failed to create LCCN Subscription.')) + return False + + return True diff --git a/tacker_horizon/openstack_dashboard/dashboards/nfv/lccnsubscription/panel.py b/tacker_horizon/openstack_dashboard/dashboards/nfv/lccnsubscription/panel.py new file mode 100644 index 0000000..9d019f5 --- /dev/null +++ b/tacker_horizon/openstack_dashboard/dashboards/nfv/lccnsubscription/panel.py @@ -0,0 +1,29 @@ +# Copyright (C) 2024 Fujitsu +# All Rights Reserved. +# +# 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.utils.translation import gettext_lazy as _ + +import horizon + +from tacker_horizon.openstack_dashboard.dashboards.nfv import dashboard + + +class Lccnsubscription(horizon.Panel): + name = _("LCCN Subscription") + slug = "lccnsubscription" + + +dashboard.Nfv.register(Lccnsubscription) diff --git a/tacker_horizon/openstack_dashboard/dashboards/nfv/lccnsubscription/tables.py b/tacker_horizon/openstack_dashboard/dashboards/nfv/lccnsubscription/tables.py new file mode 100644 index 0000000..7fdade5 --- /dev/null +++ b/tacker_horizon/openstack_dashboard/dashboards/nfv/lccnsubscription/tables.py @@ -0,0 +1,73 @@ +# Copyright (C) 2024 Fujitsu +# All Rights Reserved. +# +# 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.utils.translation import gettext_lazy as _ +from django.utils.translation import ngettext_lazy + +from horizon import tables +from openstack_dashboard import policy + +from tacker_horizon.openstack_dashboard import api + + +class MyFilterAction(tables.FilterAction): + name = "myfilter" + + +class DeleteLccnSubscription(policy.PolicyTargetMixin, tables.DeleteAction): + @staticmethod + def action_present(count): + return ngettext_lazy( + "Delete Lccn Subscription", + "Delete Lccn Subscriptions", + count + ) + + @staticmethod + def action_past(count): + return ngettext_lazy( + "Delete Lccn Subscription", + "Delete Lccn Subscriptions", + count + ) + + def action(self, request, obj_id): + api.tacker.delete_vnf_lcm_subscription(request, obj_id) + + +class CreateLccnSubscription(tables.LinkAction): + name = "create_lccnsubsc" + verbose_name = _("Create Lccn Subscription") + classes = ("ajax-modal",) + icon = "plus" + url = "horizon:nfv:lccnsubscription:createlccnsubscription" + + +class LccnSubscriptionTable(tables.DataTable): + id = tables.Column('id', verbose_name=_("ID"), + link="horizon:nfv:lccnsubscription:detail",) + callback_uri = tables.Column('callback_uri', + verbose_name=_("Callback URI")) + + class Meta(object): + name = "lccnsubsc" + verbose_name = _("LCCN Subscription") + pagination_param = 'subsc_marker' + prev_pagination_param = 'prev_subsc_marker' + table_actions = (CreateLccnSubscription, DeleteLccnSubscription, + MyFilterAction) + row_actions = (DeleteLccnSubscription,) + multi_select = True diff --git a/tacker_horizon/openstack_dashboard/dashboards/nfv/lccnsubscription/tabs.py b/tacker_horizon/openstack_dashboard/dashboards/nfv/lccnsubscription/tabs.py new file mode 100644 index 0000000..aa4b8ff --- /dev/null +++ b/tacker_horizon/openstack_dashboard/dashboards/nfv/lccnsubscription/tabs.py @@ -0,0 +1,109 @@ +# Copyright (C) 2024 Fujitsu +# All Rights Reserved. +# +# 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.utils.translation import gettext_lazy as _ + +from horizon import exceptions +from horizon import tabs +from horizon import utils as horizon_utils + +from tacker_horizon.openstack_dashboard import api +from tacker_horizon.openstack_dashboard.dashboards.nfv.lccnsubscription \ + import tables + + +class LccnSubscItem(object): + def __init__(self, subsc_id, callback_uri): + self.id = subsc_id + self.name = subsc_id + self.callback_uri = callback_uri + + +class LccnSubscriptionTab(tabs.TableTab): + name = _("LCCN Subscription tab") + slug = "lccnsubsc_tab" + table_classes = (tables.LccnSubscriptionTable,) + template_name = ("horizon/common/_detail_table.html") + preload = False + + def has_prev_data(self, table): + return self._has_prev + + def has_more_data(self, table): + return self._has_more + + def get_lccnsubsc_data(self): + try: + self._has_prev = False + page_size = horizon_utils.functions.get_page_size(self.request) + marker = self.request.GET.get( + "subsc_marker", None) + prev_marker = self.request.GET.get( + "prev_subsc_marker", None) + subscs = api.tacker.list_vnf_lcm_subscriptions( + self.request) + if marker is not None or prev_marker is not None: + for i, subsc in enumerate(subscs): + if subsc["id"] == marker and i < len(subscs) - 1: + subscs = subscs[i + 1:] + self._has_prev = True + break + if subsc["id"] == prev_marker and i > page_size: + subscs = subscs[i - page_size:] + self._has_prev = True + break + + if len(subscs) > page_size: + self._has_more = True + else: + self._has_more = False + + rows = [] + for i, subsc in enumerate(subscs): + if i >= page_size: + break + item = LccnSubscItem( + subsc_id=subsc.get('id', ''), + callback_uri=subsc.get('callbackUri', '')) + rows.append(item) + return rows + except Exception: + self._has_more = False + error_message = _('Failed to get LCCN Subscriptions.') + exceptions.handle(self.request, error_message) + + return [] + + +class LccnSubscriptionTabs(tabs.TabGroup): + slug = "lccnsubsc_tabs" + tabs = (LccnSubscriptionTab,) + sticky = True + + +class LccnSubscDetailTab(tabs.Tab): + name = _("LCCN Subscription Detail Tab") + slug = "lccnsubsc_detail_tab" + template_name = "nfv/lccnsubscription/lccnsubsc_detail.html" + + def get_context_data(self, request): + return {'lccnsubscription': self.tab_group.kwargs['lccnsubscription']} + + +class LccnSubscDetailTabs(tabs.TabGroup): + slug = "lccnsubsc_detail_tabs" + tabs = (LccnSubscDetailTab,) + sticky = True diff --git a/tacker_horizon/openstack_dashboard/dashboards/nfv/lccnsubscription/templates/lccnsubscription/_create_lccn_subscription.html b/tacker_horizon/openstack_dashboard/dashboards/nfv/lccnsubscription/templates/lccnsubscription/_create_lccn_subscription.html new file mode 100644 index 0000000..1bc5df3 --- /dev/null +++ b/tacker_horizon/openstack_dashboard/dashboards/nfv/lccnsubscription/templates/lccnsubscription/_create_lccn_subscription.html @@ -0,0 +1,9 @@ +{% extends "horizon/common/_modal_form.html" %} +{% load i18n %} + +{% block form_attrs %}enctype="multipart/form-data"{% endblock %} + +{% block modal-body-right %} +

{% trans "Description:" %}

+

{% trans "Create a Lccn Subscription." %}

+{% endblock %} diff --git a/tacker_horizon/openstack_dashboard/dashboards/nfv/lccnsubscription/templates/lccnsubscription/create_lccn_subscription.html b/tacker_horizon/openstack_dashboard/dashboards/nfv/lccnsubscription/templates/lccnsubscription/create_lccn_subscription.html new file mode 100644 index 0000000..9196320 --- /dev/null +++ b/tacker_horizon/openstack_dashboard/dashboards/nfv/lccnsubscription/templates/lccnsubscription/create_lccn_subscription.html @@ -0,0 +1,11 @@ +{% extends 'base.html' %} +{% load i18n %} +{% block title %}{% trans "create_lccn_subscription" %}{% endblock %} + +{% block page_header %} + {% include "horizon/common/_page_header.html" with title=_("create_lccn_subscription") %} +{% endblock page_header %} + +{% block main %} + {% include 'nfv/lccnsubscription/_create_lccn_subscription.html' %} +{% endblock %} diff --git a/tacker_horizon/openstack_dashboard/dashboards/nfv/lccnsubscription/templates/lccnsubscription/detail.html b/tacker_horizon/openstack_dashboard/dashboards/nfv/lccnsubscription/templates/lccnsubscription/detail.html new file mode 100644 index 0000000..b355e2a --- /dev/null +++ b/tacker_horizon/openstack_dashboard/dashboards/nfv/lccnsubscription/templates/lccnsubscription/detail.html @@ -0,0 +1,15 @@ +{% extends 'base.html' %} +{% load i18n %} +{% block title %}{% trans "Lccn Subscription Detail" %}{% endblock %} + +{% block page_header %} + {% include "horizon/common/_page_header.html" with title=page_title %} +{% endblock page_header %} + +{% block main %} +
+
+ {{ tab_group.render }} +
+
+{% endblock %} diff --git a/tacker_horizon/openstack_dashboard/dashboards/nfv/lccnsubscription/templates/lccnsubscription/index.html b/tacker_horizon/openstack_dashboard/dashboards/nfv/lccnsubscription/templates/lccnsubscription/index.html new file mode 100644 index 0000000..d901fee --- /dev/null +++ b/tacker_horizon/openstack_dashboard/dashboards/nfv/lccnsubscription/templates/lccnsubscription/index.html @@ -0,0 +1,15 @@ +{% extends 'base.html' %} +{% load i18n %} +{% block title %}{% trans "LCCN Subscription" %}{% endblock %} + +{% block page_header %} + {% include "horizon/common/_page_header.html" with title=_("LCCN Subscription") %} +{% endblock page_header %} + +{% block main %} +
+
+ {{ tab_group.render }} +
+
+{% endblock %} diff --git a/tacker_horizon/openstack_dashboard/dashboards/nfv/lccnsubscription/templates/lccnsubscription/lccnsubsc_detail.html b/tacker_horizon/openstack_dashboard/dashboards/nfv/lccnsubscription/templates/lccnsubscription/lccnsubsc_detail.html new file mode 100644 index 0000000..17f70a6 --- /dev/null +++ b/tacker_horizon/openstack_dashboard/dashboards/nfv/lccnsubscription/templates/lccnsubscription/lccnsubsc_detail.html @@ -0,0 +1,14 @@ +{% load i18n %} + +
+
+
{% trans "Callback URI" %}
+
{{ lccnsubscription.callbackUri }}
+
{% trans "Filter" %}
+
{{ lccnsubscription.filter }}
+
{% trans "ID" %}
+
{{ lccnsubscription.id }}
+
{% trans "Links" %}
+
{{ lccnsubscription.links }}
+
+
diff --git a/tacker_horizon/openstack_dashboard/dashboards/nfv/lccnsubscription/urls.py b/tacker_horizon/openstack_dashboard/dashboards/nfv/lccnsubscription/urls.py new file mode 100644 index 0000000..3181710 --- /dev/null +++ b/tacker_horizon/openstack_dashboard/dashboards/nfv/lccnsubscription/urls.py @@ -0,0 +1,29 @@ +# Copyright (C) 2024 Fujitsu +# All Rights Reserved. +# +# 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.urls import re_path + +from tacker_horizon.openstack_dashboard.dashboards.nfv.lccnsubscription \ + import views + +urlpatterns = [ + re_path(r'^$', views.IndexView.as_view(), name='index'), + re_path(r'^(?P[^/]+)/$', views.DetailView.as_view(), + name='detail'), + re_path(r'^createlccnsubscription$', + views.CreateLccnSubscriptionView.as_view(), + name='createlccnsubscription'), +] diff --git a/tacker_horizon/openstack_dashboard/dashboards/nfv/lccnsubscription/views.py b/tacker_horizon/openstack_dashboard/dashboards/nfv/lccnsubscription/views.py new file mode 100644 index 0000000..2429880 --- /dev/null +++ b/tacker_horizon/openstack_dashboard/dashboards/nfv/lccnsubscription/views.py @@ -0,0 +1,88 @@ +# Copyright (C) 2024 Fujitsu +# All Rights Reserved. +# +# 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.urls import reverse +from django.urls import reverse_lazy +from django.utils.translation import gettext_lazy as _ + +from horizon import exceptions +from horizon import forms +from horizon import tabs +from horizon.utils import memoized + +from tacker_horizon.openstack_dashboard import api as tacker_api +from tacker_horizon.openstack_dashboard.dashboards.nfv.lccnsubscription \ + import forms as project_forms +from tacker_horizon.openstack_dashboard.dashboards.nfv.lccnsubscription \ + import tabs as lccnsubsctabs + + +class IndexView(tabs.TabbedTableView): + tab_group_class = lccnsubsctabs.LccnSubscriptionTabs + template_name = 'nfv/lccnsubscription/index.html' + + +class CreateLccnSubscriptionView(forms.ModalFormView): + form_class = project_forms.CreateLccnSubscription + template_name = 'nfv/lccnsubscription/create_lccn_subscription.html' + success_url = reverse_lazy("horizon:nfv:lccnsubscription:index") + modal_id = "add_service_modal" + modal_header = _("Create Lccn Subscription") + submit_label = _("Create Lccn Subscription") + submit_url = "horizon:nfv:lccnsubscription:createlccnsubscription" + + def get_context_data(self, **kwargs): + context = super( + CreateLccnSubscriptionView, self).get_context_data(**kwargs) + context['submit_url'] = reverse(self.submit_url) + return context + + +class DetailView(tabs.TabView): + tab_group_class = lccnsubsctabs.LccnSubscDetailTabs + template_name = 'nfv/lccnsubscription/detail.html' + redirect_url = 'horizon:nfv:lccnsubscription:index' + page_title = _("LCCN Subscription Detail") + + def get_context_data(self, **kwargs): + context = super(DetailView, self).get_context_data(**kwargs) + lccnsubscription = self.get_data() + context['lccnsubscription'] = lccnsubscription + context['id'] = kwargs['id'] + context['url'] = reverse(self.redirect_url) + return context + + @memoized.memoized_method + def get_data(self): + subsc_id = self.kwargs.get('id', None) + + try: + lccnsubscription = tacker_api.tacker.get_vnf_lcm_subscription( + self.request, subsc_id) + lccnsubscription['links'] = lccnsubscription.get('_links', '') + return lccnsubscription + except Exception as e: + redirect = reverse(self.redirect_url) + exceptions.handle( + self.request, + _('Failed to get LCCN Subscription. (id: %s)') % subsc_id, + redirect=redirect) + raise exceptions.Http302(redirect) from e + + def get_tabs(self, request, *args, **kwargs): + lccnsubscription = self.get_data() + return self.tab_group_class( + request, lccnsubscription=lccnsubscription, **kwargs) diff --git a/tacker_horizon/openstack_dashboard/dashboards/nfv/vnffmalarm/__init__.py b/tacker_horizon/openstack_dashboard/dashboards/nfv/vnffmalarm/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tacker_horizon/openstack_dashboard/dashboards/nfv/vnffmalarm/forms.py b/tacker_horizon/openstack_dashboard/dashboards/nfv/vnffmalarm/forms.py new file mode 100644 index 0000000..f4afded --- /dev/null +++ b/tacker_horizon/openstack_dashboard/dashboards/nfv/vnffmalarm/forms.py @@ -0,0 +1,52 @@ +# Copyright (C) 2024 Fujitsu +# All Rights Reserved. +# +# 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.utils.translation import gettext_lazy as _ + +from horizon import exceptions +from horizon import forms +from horizon import messages + +from tacker_horizon.openstack_dashboard import api + + +class UpdateVNFFMAlarm(forms.SelfHandlingForm): + ack_state = forms.ChoiceField( + label=_('ACK State'), + choices=[('ACKNOWLEDGED', _('ACKNOWLEDGED')), + ('UNACKNOWLEDGED', _('UNACKNOWLEDGED'))]) + + def __init__(self, request, *args, **kwargs): + super(UpdateVNFFMAlarm, self).__init__(request, *args, **kwargs) + + def clean(self): + data = super(UpdateVNFFMAlarm, self).clean() + return data + + def handle(self, request, data): + try: + ack_state = data.get('ack_state', None) + + body = {} + body['ackState'] = ack_state + alarm_id = request.resolver_match.kwargs['id'] + api.tacker.update_fm_alarm(request, alarm_id, body) + messages.success(request, + _('Update FM Alarm. (id: %s)') % alarm_id) + return True + except Exception: + msg = _('Failed to update FM Alarm. (id: %s)') % alarm_id + exceptions.handle(request, msg) + return False diff --git a/tacker_horizon/openstack_dashboard/dashboards/nfv/vnffmalarm/panel.py b/tacker_horizon/openstack_dashboard/dashboards/nfv/vnffmalarm/panel.py new file mode 100644 index 0000000..8868694 --- /dev/null +++ b/tacker_horizon/openstack_dashboard/dashboards/nfv/vnffmalarm/panel.py @@ -0,0 +1,29 @@ +# Copyright (C) 2024 Fujitsu +# All Rights Reserved. +# +# 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.utils.translation import gettext_lazy as _ + +import horizon + +from tacker_horizon.openstack_dashboard.dashboards.nfv import dashboard + + +class VnffmAlarm(horizon.Panel): + name = _("Alarm") + slug = "vnffmalarm" + + +dashboard.Nfv.register(VnffmAlarm) diff --git a/tacker_horizon/openstack_dashboard/dashboards/nfv/vnffmalarm/tables.py b/tacker_horizon/openstack_dashboard/dashboards/nfv/vnffmalarm/tables.py new file mode 100644 index 0000000..8af1b41 --- /dev/null +++ b/tacker_horizon/openstack_dashboard/dashboards/nfv/vnffmalarm/tables.py @@ -0,0 +1,52 @@ +# Copyright (C) 2024 Fujitsu +# All Rights Reserved. +# +# 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.utils.translation import gettext_lazy as _ + +from horizon import tables + + +class MyFilterAction(tables.FilterAction): + name = "myfilter" + + +class UpdateVNFFMAlarm(tables.LinkAction): + name = "updatealarm" + verbose_name = _("Update Alarm") + url = "horizon:nfv:vnffmalarm:updatealarm" + classes = ("ajax-modal",) + + +class VNFFMAlarmTable(tables.DataTable): + id = tables.Column('id', link="horizon:nfv:vnffmalarm:detail", + verbose_name=_("ID")) + + managed_object_id = tables.Column('managed_object_id', + verbose_name=_("Managed Object ID")) + ack_state = tables.Column('ack_state', verbose_name=_("Ack State")) + event_type = tables.Column('event_type', verbose_name=_("Event Type")) + perceived_severity = tables.Column('perceived_severity', + verbose_name=_("Perceived Severity")) + probable_cause = tables.Column('probable_cause', + verbose_name=_("Probable Cause")) + + class Meta(object): + name = "alarm" + verbose_name = _("Alarm") + pagination_param = 'vnffmalarm_marker' + prev_pagination_param = 'prev_vnffmalarm_marker' + table_actions = (MyFilterAction,) + row_actions = (UpdateVNFFMAlarm,) diff --git a/tacker_horizon/openstack_dashboard/dashboards/nfv/vnffmalarm/tabs.py b/tacker_horizon/openstack_dashboard/dashboards/nfv/vnffmalarm/tabs.py new file mode 100644 index 0000000..c4c40be --- /dev/null +++ b/tacker_horizon/openstack_dashboard/dashboards/nfv/vnffmalarm/tabs.py @@ -0,0 +1,114 @@ +# Copyright (C) 2024 Fujitsu +# All Rights Reserved. +# +# 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.utils.translation import gettext_lazy as _ + +from horizon import exceptions +from horizon import tabs +from horizon import utils as horizon_utils + +from tacker_horizon.openstack_dashboard import api +from tacker_horizon.openstack_dashboard.dashboards.nfv.vnffmalarm \ + import tables + + +class VNFFMAlarmItem(object): + def __init__(self, alarm_id, managed_object_id, ack_state, event_type, + perceived_severity, probable_cause): + self.id = alarm_id + self.name = alarm_id + self.managed_object_id = managed_object_id + self.ack_state = ack_state + self.event_type = event_type + self.perceived_severity = perceived_severity + self.probable_cause = probable_cause + + +class VNFFMAlarmTab(tabs.TableTab): + name = _("VNFFMAlarm Tab") + slug = "vnffmalarm_tab" + table_classes = (tables.VNFFMAlarmTable,) + template_name = "horizon/common/_detail_table.html" + preload = False + + def has_prev_data(self, table): + return self._has_prev + + def has_more_data(self, table): + return self._has_more + + def get_alarm_data(self): + try: + self._has_prev = False + page_size = horizon_utils.functions.get_page_size(self.request) + marker = self.request.GET.get("vnffmalarm_marker", None) + prev_marker = self.request.GET.get("prev_vnffmalarm_marker", None) + alarms = api.tacker.list_fm_alarms(self.request) + + if marker is not None or prev_marker is not None: + for i, alarm in enumerate(alarms): + if alarm["id"] == marker and i < len(alarms) - 1: + alarms = alarms[i + 1:] + self._has_prev = True + break + if alarm["id"] == prev_marker and i > page_size: + alarms = alarms[i - page_size:] + self._has_prev = True + break + + if len(alarms) > page_size: + self._has_more = True + else: + self._has_more = False + + rows = [] + for i, alarm in enumerate(alarms): + if i >= page_size: + break + item = VNFFMAlarmItem(alarm['id'], + alarm['managedObjectId'], + alarm['ackState'], + alarm['eventType'], + alarm['perceivedSeverity'], + alarm['probableCause']) + rows.append(item) + return rows + except Exception: + self._has_more = False + error_message = _('Failed to get FM Alarms.') + exceptions.handle(self.request, error_message) + return [] + + +class VNFFMAlarmTabs(tabs.TabGroup): + slug = "vnffmalarm_tabs" + tabs = (VNFFMAlarmTab,) + sticky = True + + +class VNFFMAlarmDetailTab(tabs.Tab): + name = _("VNF FM Alarm") + slug = "vnffmalarm_detail_tab" + template_name = "nfv/vnffmalarm/alarm_detail.html" + + def get_context_data(self, request): + return {'alarm': self.tab_group.kwargs['alarm']} + + +class VNFFMAlarmDetailTabs(tabs.TabGroup): + slug = "vnffmalarm_detail_tabs" + tabs = (VNFFMAlarmDetailTab,) + sticky = True diff --git a/tacker_horizon/openstack_dashboard/dashboards/nfv/vnffmalarm/templates/vnffmalarm/_update_alarm.html b/tacker_horizon/openstack_dashboard/dashboards/nfv/vnffmalarm/templates/vnffmalarm/_update_alarm.html new file mode 100644 index 0000000..deb89d6 --- /dev/null +++ b/tacker_horizon/openstack_dashboard/dashboards/nfv/vnffmalarm/templates/vnffmalarm/_update_alarm.html @@ -0,0 +1,9 @@ +{% extends "horizon/common/_modal_form.html" %} +{% load i18n %} + +{% block form_attrs %}enctype="multipart/form-data"{% endblock %} + +{% block modal-body-right %} +

{% trans "Description:" %}

+

{% trans "Update a Alarm." %}

+{% endblock %} diff --git a/tacker_horizon/openstack_dashboard/dashboards/nfv/vnffmalarm/templates/vnffmalarm/alarm_detail.html b/tacker_horizon/openstack_dashboard/dashboards/nfv/vnffmalarm/templates/vnffmalarm/alarm_detail.html new file mode 100644 index 0000000..c105ddd --- /dev/null +++ b/tacker_horizon/openstack_dashboard/dashboards/nfv/vnffmalarm/templates/vnffmalarm/alarm_detail.html @@ -0,0 +1,42 @@ +{% load i18n %} + +
+
+
{% trans "Ack State" %}
+
{{ alarm.ackState }}
+
{% trans "Alarm Acknowledged Time" %}
+
{{ alarm.alarmAcknowledgedTime }}
+
{% trans "Alarm Changed Time" %}
+
{{ alarm.alarmChangedTime }}
+
{% trans "Alarm Cleared Time" %}
+
{{ alarm.alarmClearedTime }}
+
{% trans "Alarm Raised Time" %}
+
{{ alarm.alarmRaisedTime }}
+
{% trans "Correlated Alarm IDs" %}
+
{{ alarm.correlatedAlarmIds }}
+
{% trans "Event Time" %}
+
{{ alarm.eventTime }}
+
{% trans "Event Type" %}
+
{{ alarm.eventType }}
+
{% trans "Fault Details" %}
+
{{ alarm.faultDetails }}
+
{% trans "Fault Type" %}
+
{{ alarm.faultType }}
+
{% trans "ID" %}
+
{{ alarm.id }}
+
{% trans "Is Root Cause" %}
+
{{ alarm.isRootCause }}
+
{% trans "Links" %}
+
{{ alarm.links }}
+
{% trans "Managed Object ID" %}
+
{{ alarm.managedObjectId }}
+
{% trans "Perceived Severity" %}
+
{{ alarm.perceivedSeverity }}
+
{% trans "Probable Cause" %}
+
{{ alarm.probableCause }}
+
{% trans "Root Cause Faulty Resource" %}
+
{{ alarm.rootCauseFaultyResource }}
+
{% trans "VNFC Instance IDs" %}
+
{{ alarm.vnfcInstanceIds }}
+
+
diff --git a/tacker_horizon/openstack_dashboard/dashboards/nfv/vnffmalarm/templates/vnffmalarm/detail.html b/tacker_horizon/openstack_dashboard/dashboards/nfv/vnffmalarm/templates/vnffmalarm/detail.html new file mode 100644 index 0000000..1f284a8 --- /dev/null +++ b/tacker_horizon/openstack_dashboard/dashboards/nfv/vnffmalarm/templates/vnffmalarm/detail.html @@ -0,0 +1,15 @@ +{% extends 'base.html' %} +{% load i18n %} +{% block title %}{% trans "Alarm Detail" %}{% endblock %} + +{% block page_header %} + {% include "horizon/common/_page_header.html" with title=page_title %} +{% endblock page_header %} + +{% block main %} +
+
+ {{ tab_group.render }} +
+
+{% endblock %} diff --git a/tacker_horizon/openstack_dashboard/dashboards/nfv/vnffmalarm/templates/vnffmalarm/index.html b/tacker_horizon/openstack_dashboard/dashboards/nfv/vnffmalarm/templates/vnffmalarm/index.html new file mode 100644 index 0000000..fba9761 --- /dev/null +++ b/tacker_horizon/openstack_dashboard/dashboards/nfv/vnffmalarm/templates/vnffmalarm/index.html @@ -0,0 +1,15 @@ +{% extends 'base.html' %} +{% load i18n %} +{% block title %}{% trans "Alarm" %}{% endblock %} + +{% block page_header %} + {% include "horizon/common/_page_header.html" with title=_("Alarm") %} +{% endblock page_header %} + +{% block main %} +
+
+ {{ tab_group.render }} +
+
+{% endblock %} diff --git a/tacker_horizon/openstack_dashboard/dashboards/nfv/vnffmalarm/templates/vnffmalarm/update_alarm.html b/tacker_horizon/openstack_dashboard/dashboards/nfv/vnffmalarm/templates/vnffmalarm/update_alarm.html new file mode 100644 index 0000000..39018e0 --- /dev/null +++ b/tacker_horizon/openstack_dashboard/dashboards/nfv/vnffmalarm/templates/vnffmalarm/update_alarm.html @@ -0,0 +1,11 @@ +{% extends 'base.html' %} +{% load i18n %} +{% block title %}{% trans "Update Alarm" %}{% endblock %} + +{% block page_header %} + {% include "horizon/common/_page_header.html" with title=_("Update Alarm") %} +{% endblock page_header %} + +{% block main %} + {% include 'nfv/vnffmalarm/_update_alarm.html' %} +{% endblock %} diff --git a/tacker_horizon/openstack_dashboard/dashboards/nfv/vnffmalarm/urls.py b/tacker_horizon/openstack_dashboard/dashboards/nfv/vnffmalarm/urls.py new file mode 100644 index 0000000..03d9c7c --- /dev/null +++ b/tacker_horizon/openstack_dashboard/dashboards/nfv/vnffmalarm/urls.py @@ -0,0 +1,29 @@ +# Copyright (C) 2024 Fujitsu +# All Rights Reserved. +# +# 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.urls import re_path + +from tacker_horizon.openstack_dashboard.dashboards.nfv.vnffmalarm \ + import views + +urlpatterns = [ + re_path(r'^$', views.IndexView.as_view(), name='index'), + re_path(r'^(?P[^/]+)/updatealarm$', + views.UpdateVNFFMAlarmView.as_view(), + name='updatealarm'), + re_path(r'^(?P[^/]+)/$', views.DetailView.as_view(), + name='detail'), +] diff --git a/tacker_horizon/openstack_dashboard/dashboards/nfv/vnffmalarm/views.py b/tacker_horizon/openstack_dashboard/dashboards/nfv/vnffmalarm/views.py new file mode 100644 index 0000000..59f40a3 --- /dev/null +++ b/tacker_horizon/openstack_dashboard/dashboards/nfv/vnffmalarm/views.py @@ -0,0 +1,90 @@ +# Copyright (C) 2024 Fujitsu +# All Rights Reserved. +# +# 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.urls import reverse +from django.urls import reverse_lazy +from django.utils.translation import gettext_lazy as _ + +from horizon import exceptions +from horizon import forms +from horizon import tabs +from horizon.utils import memoized + +from tacker_horizon.openstack_dashboard import api as tacker_api +from tacker_horizon.openstack_dashboard.dashboards.nfv.vnffmalarm \ + import forms as project_forms +from tacker_horizon.openstack_dashboard.dashboards.nfv.vnffmalarm \ + import tabs as alarm_tabs + + +class IndexView(tabs.TabbedTableView): + # A very simple class-based view... + tab_group_class = alarm_tabs.VNFFMAlarmTabs + template_name = 'nfv/vnffmalarm/index.html' + + def get_data(self, request, context, *args, **kwargs): + # Add data to the context here... + return context + + +class UpdateVNFFMAlarmView(forms.ModalFormView): + form_class = project_forms.UpdateVNFFMAlarm + template_name = 'nfv/vnffmalarm/update_alarm.html' + success_url = reverse_lazy("horizon:nfv:vnffmalarm:index") + modal_id = "update_alarm_modal" + modal_header = _("Update Alarm") + submit_label = _("Update Alarm") + submit_url = "horizon:nfv:vnffmalarm:updatealarm" + + def get_context_data(self, **kwargs): + context = super(UpdateVNFFMAlarmView, self).get_context_data(**kwargs) + context['submit_url'] = reverse(self.submit_url, + kwargs={'id': self.kwargs['id']}) + return context + + +class DetailView(tabs.TabView): + tab_group_class = alarm_tabs.VNFFMAlarmDetailTabs + template_name = 'nfv/vnffmalarm/detail.html' + redirect_url = 'horizon:nfv:vnffmalarm:index' + page_title = _("Alarm Detail: {{ id }}") + + def get_context_data(self, **kwargs): + context = super(DetailView, self).get_context_data(**kwargs) + alarm = self.get_data() + context['alarm'] = alarm + context['id'] = kwargs['id'] + context['url'] = reverse(self.redirect_url) + return context + + @memoized.memoized_method + def get_data(self): + alarm_id = self.kwargs['id'] + + try: + alarm = tacker_api.tacker.get_fm_alarm(self.request, alarm_id) + alarm['links'] = alarm.get('_links', '') + return alarm + except Exception: + redirect = reverse(self.redirect_url) + exceptions.handle(self.request, + _('Failed to get FM Alarm. (id: %s)') % alarm_id, + redirect=redirect) + raise exceptions.Http302(redirect) + + def get_tabs(self, request, *args, **kwargs): + alarm = self.get_data() + return self.tab_group_class(request, alarm=alarm, **kwargs) diff --git a/tacker_horizon/openstack_dashboard/dashboards/nfv/vnffmsubscription/__init__.py b/tacker_horizon/openstack_dashboard/dashboards/nfv/vnffmsubscription/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tacker_horizon/openstack_dashboard/dashboards/nfv/vnffmsubscription/forms.py b/tacker_horizon/openstack_dashboard/dashboards/nfv/vnffmsubscription/forms.py new file mode 100644 index 0000000..8c47090 --- /dev/null +++ b/tacker_horizon/openstack_dashboard/dashboards/nfv/vnffmsubscription/forms.py @@ -0,0 +1,64 @@ +# Copyright (C) 2024 Fujitsu +# All Rights Reserved. +# +# 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. + +import json + +from django.utils.translation import gettext_lazy as _ + +from horizon import exceptions +from horizon import forms +from horizon import messages + +from tacker_horizon.openstack_dashboard import api + + +class CreateVNFFMSubscription(forms.SelfHandlingForm): + param_file = forms.FileField( + label=_("Param File"), + help_text=_("parameter file to upload."), + widget=forms.FileInput( + attrs={'class': 'switched', + 'data-switch-on': 'source', + 'data-source-file': _('Param File')})) + + def __init__(self, request, *args, **kwargs): + super(CreateVNFFMSubscription, self).__init__(request, *args, **kwargs) + + def clean(self): + data = super(CreateVNFFMSubscription, self).clean() + + try: + param_str = self.files['param_file'].read() + param = json.loads(param_str) + data['param'] = param + except Exception as e: + msg = _('Failed to read file: %s.') % e + raise forms.ValidationError(msg) + + return data + + def handle(self, request, data): + try: + param = data['param'] + subscription_instance = api.tacker.create_fm_subscription( + request, param) + messages.success(request, + _('Create FM Subscription. (id: %s)') % + subscription_instance['id']) + return True + except Exception: + msg = _('Failed to create FM Subscription.') + exceptions.handle(request, msg) + return False diff --git a/tacker_horizon/openstack_dashboard/dashboards/nfv/vnffmsubscription/panel.py b/tacker_horizon/openstack_dashboard/dashboards/nfv/vnffmsubscription/panel.py new file mode 100644 index 0000000..83e17eb --- /dev/null +++ b/tacker_horizon/openstack_dashboard/dashboards/nfv/vnffmsubscription/panel.py @@ -0,0 +1,29 @@ +# Copyright (C) 2024 Fujitsu +# All Rights Reserved. +# +# 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.utils.translation import gettext_lazy as _ + +import horizon + +from tacker_horizon.openstack_dashboard.dashboards.nfv import dashboard + + +class VnffmSubscription(horizon.Panel): + name = _("Subscription") + slug = "vnffmsubscription" + + +dashboard.Nfv.register(VnffmSubscription) diff --git a/tacker_horizon/openstack_dashboard/dashboards/nfv/vnffmsubscription/tables.py b/tacker_horizon/openstack_dashboard/dashboards/nfv/vnffmsubscription/tables.py new file mode 100644 index 0000000..41c30b3 --- /dev/null +++ b/tacker_horizon/openstack_dashboard/dashboards/nfv/vnffmsubscription/tables.py @@ -0,0 +1,72 @@ +# Copyright (C) 2024 Fujitsu +# All Rights Reserved. +# +# 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.utils.translation import gettext_lazy as _ +from django.utils.translation import ngettext_lazy + +from horizon import tables +from openstack_dashboard import policy + +from tacker_horizon.openstack_dashboard import api + + +class MyFilterAction(tables.FilterAction): + name = "myfilter" + + +class DeleteVNFFMSubscription(policy.PolicyTargetMixin, tables.DeleteAction): + @staticmethod + def action_present(count): + return ngettext_lazy( + "Delete Subscription", + "Delete Subscriptions", + count + ) + + @staticmethod + def action_past(count): + return ngettext_lazy( + "Delete Subscription", + "Delete Subscriptions", + count + ) + + def action(self, request, obj_id): + api.tacker.delete_fm_subscription(request, obj_id) + + +class CreateVNFFMSubscription(tables.LinkAction): + name = "createsubscription" + verbose_name = _("Create Subscription") + classes = ("ajax-modal",) + icon = "plus" + url = "horizon:nfv:vnffmsubscription:createsubscription" + + +class VNFFMSubscriptionTable(tables.DataTable): + id = tables.Column('id', link="horizon:nfv:vnffmsubscription:detail", + verbose_name=_("ID")) + callback_uri = tables.Column('callback_uri', + verbose_name=_("Callback Uri")) + + class Meta(object): + name = "subscription" + verbose_name = _("Subscription") + pagination_param = 'vnffmsubscription_marker' + prev_pagination_param = 'prev_vnffmsubscription_marker' + table_actions = (CreateVNFFMSubscription, DeleteVNFFMSubscription, + MyFilterAction,) + row_actions = (DeleteVNFFMSubscription,) diff --git a/tacker_horizon/openstack_dashboard/dashboards/nfv/vnffmsubscription/tabs.py b/tacker_horizon/openstack_dashboard/dashboards/nfv/vnffmsubscription/tabs.py new file mode 100644 index 0000000..ff8b34a --- /dev/null +++ b/tacker_horizon/openstack_dashboard/dashboards/nfv/vnffmsubscription/tabs.py @@ -0,0 +1,106 @@ +# Copyright (C) 2024 Fujitsu +# All Rights Reserved. +# +# 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.utils.translation import gettext_lazy as _ + +from horizon import exceptions +from horizon import tabs +from horizon import utils as horizon_utils + +from tacker_horizon.openstack_dashboard import api +from tacker_horizon.openstack_dashboard.dashboards.nfv.vnffmsubscription \ + import tables + + +class VNFFMSubscriptionItem(object): + def __init__(self, subscription_id, callback_uri): + self.id = subscription_id + self.name = subscription_id + self.callback_uri = callback_uri + + +class VNFFMSubscriptionTab(tabs.TableTab): + name = _("VNFFMSubscription Tab") + slug = "vnffmsubscription_tab" + table_classes = (tables.VNFFMSubscriptionTable,) + template_name = "horizon/common/_detail_table.html" + preload = False + + def has_prev_data(self, table): + return self._has_prev + + def has_more_data(self, table): + return self._has_more + + def get_subscription_data(self): + try: + self._has_prev = False + page_size = horizon_utils.functions.get_page_size(self.request) + marker = self.request.GET.get("vnffmsubscription_marker", None) + prev_marker = self.request.GET.get( + "prev_vnffmsubscription_marker", None) + subscriptions = api.tacker.list_fm_subscriptions(self.request) + + if marker is not None or prev_marker is not None: + for i, subsc in enumerate(subscriptions): + if subsc["id"] == marker and i < len(subscriptions) - 1: + subscriptions = subscriptions[i + 1:] + self._has_prev = True + break + if subsc["id"] == prev_marker and i > page_size: + subscriptions = subscriptions[i - page_size:] + self._has_prev = True + break + + if len(subscriptions) > page_size: + self._has_more = True + else: + self._has_more = False + + rows = [] + for i, subsc in enumerate(subscriptions): + if i >= page_size: + break + item = VNFFMSubscriptionItem(subsc['id'], + subsc['callbackUri']) + rows.append(item) + return rows + except Exception: + self._has_more = False + error_message = _('Failed to get FM Subscriptions.') + exceptions.handle(self.request, error_message) + return [] + + +class VNFFMSubscriptionTabs(tabs.TabGroup): + slug = "vnffmsubscription_tabs" + tabs = (VNFFMSubscriptionTab,) + sticky = True + + +class VNFFMSubscriptionDetailTab(tabs.Tab): + name = _("VNF FM Subscription") + slug = "vnffmsubscription_detail_tab" + template_name = "nfv/vnffmsubscription/subscription_detail.html" + + def get_context_data(self, request): + return {'subscription': self.tab_group.kwargs['subscription']} + + +class VNFFMSubscriptionDetailTabs(tabs.TabGroup): + slug = "vnffmsubscription_detail_tabs" + tabs = (VNFFMSubscriptionDetailTab,) + sticky = True diff --git a/tacker_horizon/openstack_dashboard/dashboards/nfv/vnffmsubscription/templates/vnffmsubscription/_create_subscription.html b/tacker_horizon/openstack_dashboard/dashboards/nfv/vnffmsubscription/templates/vnffmsubscription/_create_subscription.html new file mode 100644 index 0000000..88406dc --- /dev/null +++ b/tacker_horizon/openstack_dashboard/dashboards/nfv/vnffmsubscription/templates/vnffmsubscription/_create_subscription.html @@ -0,0 +1,9 @@ +{% extends "horizon/common/_modal_form.html" %} +{% load i18n %} + +{% block form_attrs %}enctype="multipart/form-data"{% endblock %} + +{% block modal-body-right %} +

{% trans "Description:" %}

+

{% trans "Create a Subscription." %}

+{% endblock %} diff --git a/tacker_horizon/openstack_dashboard/dashboards/nfv/vnffmsubscription/templates/vnffmsubscription/create_subscription.html b/tacker_horizon/openstack_dashboard/dashboards/nfv/vnffmsubscription/templates/vnffmsubscription/create_subscription.html new file mode 100644 index 0000000..acb3486 --- /dev/null +++ b/tacker_horizon/openstack_dashboard/dashboards/nfv/vnffmsubscription/templates/vnffmsubscription/create_subscription.html @@ -0,0 +1,11 @@ +{% extends 'base.html' %} +{% load i18n %} +{% block title %}{% trans "Create Subscription" %}{% endblock %} + +{% block page_header %} + {% include "horizon/common/_page_header.html" with title=_("Create Subscription") %} +{% endblock page_header %} + +{% block main %} + {% include 'nfv/vnffmsubscription/_create_subscription.html' %} +{% endblock %} diff --git a/tacker_horizon/openstack_dashboard/dashboards/nfv/vnffmsubscription/templates/vnffmsubscription/detail.html b/tacker_horizon/openstack_dashboard/dashboards/nfv/vnffmsubscription/templates/vnffmsubscription/detail.html new file mode 100644 index 0000000..55b3c5c --- /dev/null +++ b/tacker_horizon/openstack_dashboard/dashboards/nfv/vnffmsubscription/templates/vnffmsubscription/detail.html @@ -0,0 +1,15 @@ +{% extends 'base.html' %} +{% load i18n %} +{% block title %}{% trans "Subscription Detail" %}{% endblock %} + +{% block page_header %} + {% include "horizon/common/_page_header.html" with title=page_title %} +{% endblock page_header %} + +{% block main %} +
+
+ {{ tab_group.render }} +
+
+{% endblock %} diff --git a/tacker_horizon/openstack_dashboard/dashboards/nfv/vnffmsubscription/templates/vnffmsubscription/index.html b/tacker_horizon/openstack_dashboard/dashboards/nfv/vnffmsubscription/templates/vnffmsubscription/index.html new file mode 100644 index 0000000..94dff09 --- /dev/null +++ b/tacker_horizon/openstack_dashboard/dashboards/nfv/vnffmsubscription/templates/vnffmsubscription/index.html @@ -0,0 +1,15 @@ +{% extends 'base.html' %} +{% load i18n %} +{% block title %}{% trans "Subscription" %}{% endblock %} + +{% block page_header %} + {% include "horizon/common/_page_header.html" with title=_("Subscription") %} +{% endblock page_header %} + +{% block main %} +
+
+ {{ tab_group.render }} +
+
+{% endblock %} diff --git a/tacker_horizon/openstack_dashboard/dashboards/nfv/vnffmsubscription/templates/vnffmsubscription/subscription_detail.html b/tacker_horizon/openstack_dashboard/dashboards/nfv/vnffmsubscription/templates/vnffmsubscription/subscription_detail.html new file mode 100644 index 0000000..12c571c --- /dev/null +++ b/tacker_horizon/openstack_dashboard/dashboards/nfv/vnffmsubscription/templates/vnffmsubscription/subscription_detail.html @@ -0,0 +1,14 @@ +{% load i18n %} + +
+
+
{% trans "ID" %}
+
{{ subscription.id }}
+
{% trans "Links" %}
+
{{ subscription.links }}
+
{% trans "Callback Uri" %}
+
{{ subscription.callback_uri }}
+
{% trans "Filter" %}
+
{{ subscription.filter }}
+
+
diff --git a/tacker_horizon/openstack_dashboard/dashboards/nfv/vnffmsubscription/urls.py b/tacker_horizon/openstack_dashboard/dashboards/nfv/vnffmsubscription/urls.py new file mode 100644 index 0000000..ec367cf --- /dev/null +++ b/tacker_horizon/openstack_dashboard/dashboards/nfv/vnffmsubscription/urls.py @@ -0,0 +1,29 @@ +# Copyright (C) 2024 Fujitsu +# All Rights Reserved. +# +# 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.urls import re_path + +from tacker_horizon.openstack_dashboard.dashboards.nfv.vnffmsubscription \ + import views + +urlpatterns = [ + re_path(r'^$', views.IndexView.as_view(), name='index'), + re_path(r'^createsubscription', + views.CreateVNFFMSubscriptionView.as_view(), + name='createsubscription'), + re_path(r'^(?P[^/]+)/$', views.DetailView.as_view(), + name='detail'), +] diff --git a/tacker_horizon/openstack_dashboard/dashboards/nfv/vnffmsubscription/views.py b/tacker_horizon/openstack_dashboard/dashboards/nfv/vnffmsubscription/views.py new file mode 100644 index 0000000..cb8dfde --- /dev/null +++ b/tacker_horizon/openstack_dashboard/dashboards/nfv/vnffmsubscription/views.py @@ -0,0 +1,94 @@ +# Copyright (C) 2024 Fujitsu +# All Rights Reserved. +# +# 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.urls import reverse +from django.urls import reverse_lazy +from django.utils.translation import gettext_lazy as _ + +from horizon import exceptions +from horizon import forms +from horizon import tabs +from horizon.utils import memoized + +from tacker_horizon.openstack_dashboard import api as tacker_api +from tacker_horizon.openstack_dashboard.dashboards.nfv.vnffmsubscription \ + import forms as project_forms +from tacker_horizon.openstack_dashboard.dashboards.nfv.vnffmsubscription \ + import tabs as subscription_tabs + + +class IndexView(tabs.TabbedTableView): + # A very simple class-based view... + tab_group_class = subscription_tabs.VNFFMSubscriptionTabs + template_name = 'nfv/vnffmsubscription/index.html' + + def get_data(self, request, context, *args, **kwargs): + # Add data to the context here... + return context + + +class CreateVNFFMSubscriptionView(forms.ModalFormView): + form_class = project_forms.CreateVNFFMSubscription + template_name = 'nfv/vnffmsubscription/create_subscription.html' + success_url = reverse_lazy("horizon:nfv:vnffmsubscription:index") + modal_id = "create_subscription_modal" + modal_header = _("Create Subscription") + submit_label = _("Create Subscription") + submit_url = "horizon:nfv:vnffmsubscription:createsubscription" + + def get_context_data(self, **kwargs): + context = super(CreateVNFFMSubscriptionView, + self).get_context_data(**kwargs) + context['submit_url'] = reverse(self.submit_url) + return context + + +class DetailView(tabs.TabView): + tab_group_class = subscription_tabs.VNFFMSubscriptionDetailTabs + template_name = 'nfv/vnffmsubscription/detail.html' + redirect_url = 'horizon:nfv:vnffmsubscription:index' + page_title = _("Subscription Detail: {{ id }}") + + def get_context_data(self, **kwargs): + context = super(DetailView, self).get_context_data(**kwargs) + subscription = self.get_data() + context['subscription'] = subscription + context['id'] = kwargs['id'] + context['url'] = reverse(self.redirect_url) + return context + + @memoized.memoized_method + def get_data(self): + subscription_id = self.kwargs['id'] + + try: + subscription = tacker_api.tacker.get_fm_subscription( + self.request, subscription_id) + subscription['links'] = subscription.get('_links', '') + subscription['callback_uri'] = subscription.get('callbackUri', '') + subscription['filter'] = subscription.get('filter', '') + return subscription + except Exception: + redirect = reverse(self.redirect_url) + exceptions.handle(self.request, + _('Failed to get FM Subscription. (id: %s)') % + subscription_id, redirect=redirect) + raise exceptions.Http302(redirect) + + def get_tabs(self, request, *args, **kwargs): + subscription = self.get_data() + return self.tab_group_class(request, + subscription=subscription, **kwargs) diff --git a/tacker_horizon/openstack_dashboard/dashboards/nfv/vnflcm/__init__.py b/tacker_horizon/openstack_dashboard/dashboards/nfv/vnflcm/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tacker_horizon/openstack_dashboard/dashboards/nfv/vnflcm/forms.py b/tacker_horizon/openstack_dashboard/dashboards/nfv/vnflcm/forms.py new file mode 100644 index 0000000..ab1b66e --- /dev/null +++ b/tacker_horizon/openstack_dashboard/dashboards/nfv/vnflcm/forms.py @@ -0,0 +1,425 @@ +# Copyright (C) 2024 Fujitsu +# All Rights Reserved. +# +# 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. + +import json +import time + +from django.utils.translation import gettext_lazy as _ + +from horizon import exceptions +from horizon import forms +from horizon import messages + +from tacker_horizon.openstack_dashboard import api + + +class CreateVnfIdentifier(forms.SelfHandlingForm): + vnfd_id = forms.CharField(label=_("VNFD ID")) + param_file = forms.FileField( + label=_("Param File"), + widget=forms.FileInput(attrs={ + 'class': 'switched', + 'data-switch-on': 'source', + 'data-source-upload': _('Param File')}), + required=False) + name = forms.CharField(label=_("Name"), required=False) + description = forms.CharField(label=_("Description"), required=False) + + def __init__(self, request, *args, **kwargs): + super(CreateVnfIdentifier, self).__init__(request, *args, **kwargs) + + def clean(self): + data = super(CreateVnfIdentifier, self).clean() + + param_file = data.get('param_file', None) + + try: + if param_file: + metadata = self.files['param_file'].read() + data['metadata'] = json.loads(metadata) + except Exception as e: + msg = _('Failed to read file: %s.') % e + raise forms.ValidationError(msg) + return data + + def handle(self, request, data): + try: + vnfd_id = data.get('vnfd_id', None) + metadata = data.get('metadata', None) + name = data.get('name', None) + description = data.get('description', None) + + body = {} + body['vnfdId'] = vnfd_id + if metadata: + body['metadata'] = metadata + if name: + body['vnfInstanceName'] = name + if description: + body['vnfInstanceDescription'] = description + + response = api.tacker.create_vnf_instance(request, body) + messages.success(request, + _('Create VNF Identifier. (id: %s)') % + response['id']) + + except Exception: + exceptions.handle(request, _('Failed to create VNF Identifier.')) + return False + + return True + + +class InstantiateVnf(forms.SelfHandlingForm): + param_file = forms.FileField( + label=_("Param File"), + widget=forms.FileInput(attrs={ + 'class': 'switched', + 'data-switch-on': 'source', + 'data-source-upload': _('Param File')})) + + def __init__(self, request, *args, **kwargs): + super(InstantiateVnf, self).__init__(request, *args, **kwargs) + + def clean(self): + data = super(InstantiateVnf, self).clean() + + try: + param_str = self.files['param_file'].read() + param = json.loads(param_str) + data['param_data'] = param + except Exception as e: + msg = _('Failed to read file: %s.') % e + raise forms.ValidationError(msg) + + return data + + def handle(self, request, data): + try: + vnf_id = request.resolver_match.kwargs.get('id', None) + param = data['param_data'] + api.tacker.instantiate_vnf_instance(request, vnf_id, param) + messages.success(request, + _('Accepted to instantiate VNF Instance. ' + '(id: %s)') % vnf_id) + + except Exception: + exceptions.handle(request, + _('Failed to instantiate VNF Instance. ' + '(id: %s)') % vnf_id) + return False + + return True + + +class TerminateVnf(forms.SelfHandlingForm): + termination_type = forms.ChoiceField( + label=_('Termination Type'), + required=True, + choices=[('GRACEFUL', _('GRACEFUL')), + ('FORCEFUL', _('FORCEFUL'))], + widget=forms.Select( + attrs={'class': 'switchable', 'data-slug': 'source'})) + termination_timeout = forms.IntegerField( + min_value=0, + required=False, + label=_('Graceful Termination Timeout')) + is_delete = forms.BooleanField( + label=_('Delete VNF Instance'), + required=False) + + def __init__(self, request, *args, **kwargs): + super(TerminateVnf, self).__init__(request, *args, **kwargs) + + def clean(self): + data = super(TerminateVnf, self).clean() + return data + + def handle(self, request, data): + try: + is_delete = data.get('is_delete', False) + vnf_id = request.resolver_match.kwargs.get('id', None) + body = {} + termination_type = data.get('termination_type', None) + body['terminationType'] = termination_type + termination_timeout = data.get('terminationTimeout', None) + if termination_timeout: + body['gracefulTerminationTimeout'] = termination_timeout + api.tacker.terminate_vnf_instance(request, vnf_id, body) + if not is_delete: + messages.success(request, + _('Accepted to terminate VNF Instance. ' + '(id: %s)') % vnf_id) + except Exception: + exceptions.handle(request, + _('Failed to terminate VNF Instance. ' + '(id: %s)') % vnf_id) + return False + + if is_delete: + retry = 0 + retry_limit = 12 + while True: + try: + time.sleep(10) + api.tacker.delete_vnf_instance(request, vnf_id) + messages.success(request, + _('Delete VNF Identifier. (id: %s)') % + vnf_id) + break + except Exception as exc: + if 'is in progress' in str(exc): + # Conflict error occurs even if delete + # is executed immediately after terminate + retry = retry + 1 + if retry >= retry_limit: + exceptions.handle( + request, + _('Delete VNF Identifier retry out. (id: %s)') + % vnf_id) + break + continue + else: + exceptions.handle( + request, + _('Failed to delete VNF Identifer. (id: %s)') % + vnf_id) + break + return True + + +class HealVnf(forms.SelfHandlingForm): + cause = forms.CharField(label=_("Cause"), + required=False) + vnfc_instances = forms.CharField( + label=_('VNFC Instance'), required=False, widget=forms.Textarea, + help_text=_('Enter VNFC instance IDs in comma-separated list format.')) + + def __init__(self, request, *args, **kwargs): + super(HealVnf, self).__init__(request, *args, **kwargs) + + def clean(self): + data = super(HealVnf, self).clean() + return data + + def handle(self, request, data): + try: + vnf_id = request.resolver_match.kwargs.get('id', None) + body = {} + cause = data.get('cause', None) + if cause: + body['cause'] = cause + array_str = data.get('vnfc_instances', None) + if array_str: + array_str.replace('\n', '') + vnfc_instance_ids = [s.strip() for s in array_str.split(',')] + body['vnfcInstanceId'] = vnfc_instance_ids + api.tacker.heal_vnf_instance(request, vnf_id, body) + messages.success(request, + _('Accepted to heal VNF Instance. (id: %s)') % + vnf_id) + + except Exception: + exceptions.handle(request, + _('Failed to heal VNF Instance. (id: %s)') % + vnf_id) + return False + + return True + + +class UpdateVnf(forms.SelfHandlingForm): + param_file = forms.FileField( + label=_("Param File"), + widget=forms.FileInput(attrs={ + 'class': 'switched', + 'data-switch-on': 'source', + 'data-source-upload': _('Param File')})) + + def __init__(self, request, *args, **kwargs): + super(UpdateVnf, self).__init__(request, *args, **kwargs) + + def clean(self): + data = super(UpdateVnf, self).clean() + try: + param_str = self.files['param_file'].read() + param = json.loads(param_str) + data['param_data'] = param + except Exception as e: + msg = _('Failed to read file: %s.') % e + raise forms.ValidationError(msg) + + return data + + def handle(self, request, data): + try: + vnf_id = request.resolver_match.kwargs.get('id', None) + param = data['param_data'] + api.tacker.update_vnf_instance(request, vnf_id, param) + messages.success(request, + _('Accepted to update VNF Instance. ' + '(id: %s)') % vnf_id) + + except Exception: + exceptions.handle(request, + _('Failed to update VNF Instance. ' + '(id: %s)') % vnf_id) + return False + + return True + + +class ScaleVnf(forms.SelfHandlingForm): + scale_type = forms.ChoiceField( + label=_('Type'), + choices=[('SCALE_IN', _('SCALE_IN')), + ('SCALE_OUT', _('SCALE_OUT'))]) + aspect_id = forms.CharField(label=_("Aspect ID")) + num_of_steps = forms.IntegerField( + label=_("Number of Steps"), min_value=1, required=False) + param_file = forms.FileField( + label=_("Param File"), + widget=forms.FileInput(attrs={ + 'class': 'switched', + 'data-switch-on': 'source', + 'data-source-upload': _('Param File')}), + required=False) + + def __init__(self, request, *args, **kwargs): + super(ScaleVnf, self).__init__(request, *args, **kwargs) + + def clean(self): + data = super(ScaleVnf, self).clean() + + param_file = data.get('param_file', None) + + try: + if param_file: + additional_params = self.files['param_file'].read() + data['additional_params'] = json.loads(additional_params) + except Exception as e: + msg = _('Failed to read file: %s.') % e + raise forms.ValidationError(msg) + return data + + def handle(self, request, data): + try: + vnf_id = request.resolver_match.kwargs.get('id', None) + scale_type = data.get('scale_type', None) + aspect_id = data.get('aspect_id', None) + num_of_steps = data.get('num_of_steps', None) + additional_params = data.get('additional_params', None) + + body = {} + body['type'] = scale_type + body['aspectId'] = aspect_id + if num_of_steps: + body['numberOfSteps'] = num_of_steps + if additional_params: + body['additionalParams'] = additional_params + + api.tacker.scale_vnf_instance(request, vnf_id, body) + messages.success(request, + _('Accepted to scale VNF Instance. ' + '(id: %s)') % vnf_id) + + except Exception: + exceptions.handle(request, + _('Failed to scale VNF Instance. ' + '(id: %s)') % vnf_id) + return False + + return True + + +class ChangeExternalVnfConnectivity(forms.SelfHandlingForm): + param_file = forms.FileField( + label=_("Param File"), + widget=forms.FileInput(attrs={ + 'class': 'switched', + 'data-switch-on': 'source', + 'data-source-upload': _('Param File')})) + + def __init__(self, request, *args, **kwargs): + super(ChangeExternalVnfConnectivity, + self).__init__(request, *args, **kwargs) + + def clean(self): + data = super(ChangeExternalVnfConnectivity, self).clean() + try: + param_str = self.files['param_file'].read() + param = json.loads(param_str) + data['param_data'] = param + except Exception as e: + msg = _('Failed to read file: %s.') % e + raise forms.ValidationError(msg) + return data + + def handle(self, request, data): + try: + vnf_id = request.resolver_match.kwargs.get('id', None) + param = data['param_data'] + api.tacker.change_ext_conn_vnf_instance(request, vnf_id, param) + messages.success(request, + _('Accepted to change External VNF Connectivity. ' + '(id: %s)') % vnf_id) + + except Exception: + exceptions.handle(request, + _('Failed to change External VNF Connectivity. ' + '(id: %s)') % vnf_id) + return False + + return True + + +class ChangeCurrentVnfPackage(forms.SelfHandlingForm): + param_file = forms.FileField( + label=_("Param File"), + widget=forms.FileInput(attrs={ + 'class': 'switched', + 'data-switch-on': 'source', + 'data-source-upload': _('Param File')})) + + def __init__(self, request, *args, **kwargs): + super(ChangeCurrentVnfPackage, self).__init__(request, *args, **kwargs) + + def clean(self): + data = super(ChangeCurrentVnfPackage, self).clean() + try: + param_str = self.files['param_file'].read() + param = json.loads(param_str) + data['param_data'] = param + except Exception as e: + msg = _('Failed to read file: %s.') % e + raise forms.ValidationError(msg) + return data + + def handle(self, request, data): + try: + vnf_id = request.resolver_match.kwargs.get('id', None) + param = data['param_data'] + api.tacker.change_vnfpkg_vnf_instance(request, vnf_id, param) + messages.success(request, + _('Accepted to change Current VNF Package. ' + '(id: %s)') % vnf_id) + + except Exception: + exceptions.handle(request, + _('Failed to change Current VNF Package. ' + '(id: %s)') % vnf_id) + return False + + return True diff --git a/tacker_horizon/openstack_dashboard/dashboards/nfv/vnflcm/panel.py b/tacker_horizon/openstack_dashboard/dashboards/nfv/vnflcm/panel.py new file mode 100644 index 0000000..7ebab29 --- /dev/null +++ b/tacker_horizon/openstack_dashboard/dashboards/nfv/vnflcm/panel.py @@ -0,0 +1,29 @@ +# Copyright (C) 2024 Fujitsu +# All Rights Reserved. +# +# 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.utils.translation import gettext_lazy as _ + +import horizon + +from tacker_horizon.openstack_dashboard.dashboards.nfv import dashboard + + +class Vnflcm(horizon.Panel): + name = _("VNF LCM") + slug = "vnflcm" + + +dashboard.Nfv.register(Vnflcm) diff --git a/tacker_horizon/openstack_dashboard/dashboards/nfv/vnflcm/tables.py b/tacker_horizon/openstack_dashboard/dashboards/nfv/vnflcm/tables.py new file mode 100644 index 0000000..e2b1911 --- /dev/null +++ b/tacker_horizon/openstack_dashboard/dashboards/nfv/vnflcm/tables.py @@ -0,0 +1,133 @@ +# Copyright (C) 2024 Fujitsu +# All Rights Reserved. +# +# 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.utils.translation import gettext_lazy as _ +from django.utils.translation import ngettext_lazy + +from horizon import tables +from openstack_dashboard import policy + +from tacker_horizon.openstack_dashboard import api + + +class MyFilterAction(tables.FilterAction): + name = "myfilter" + + +class DeleteVnfIdentifier(policy.PolicyTargetMixin, tables.DeleteAction): + @staticmethod + def action_present(count): + return ngettext_lazy( + "Delete VNF Identifier", + "Delete VNF Identifiers", + count + ) + + @staticmethod + def action_past(count): + return ngettext_lazy( + "Delete VNF Identifier", + "Delete VNF Identifiers", + count + ) + + def action(self, request, obj_id): + api.tacker.delete_vnf_instance(request, obj_id) + + +class CreateVnfIdentifier(tables.LinkAction): + name = "createvnfid" + verbose_name = _("Create VNF Identifier") + url = "horizon:nfv:vnflcm:createvnfidentifier" + classes = ("ajax-modal",) + icon = "plus" + + +class InstantiateVnf(tables.LinkAction): + name = "instantiatevnf" + verbose_name = _("Instantiate VNF") + url = "horizon:nfv:vnflcm:instantiatevnf" + classes = ("ajax-modal",) + + +class TerminateVnf(tables.LinkAction): + name = "terminate_vnf" + verbose_name = _("Terminate VNF") + url = "horizon:nfv:vnflcm:terminatevnf" + classes = ("ajax-modal",) + + +class HealVnf(tables.LinkAction): + name = "heal_vnf" + verbose_name = _("Heal VNF") + url = "horizon:nfv:vnflcm:healvnf" + classes = ("ajax-modal",) + + +class UpdateVnf(tables.LinkAction): + name = "update_Vnf" + verbose_name = _("Update VNF") + url = "horizon:nfv:vnflcm:updatevnf" + classes = ("ajax-modal",) + + +class ScaleVnf(tables.LinkAction): + name = "scale_vnf" + verbose_name = _("Scale VNF") + url = "horizon:nfv:vnflcm:scalevnf" + classes = ("ajax-modal",) + + +class ChangeExternalVnfConnectivity(tables.LinkAction): + name = "change_external_vnf_connectivity" + verbose_name = _("Change External VNF Connectivity") + url = "horizon:nfv:vnflcm:changeconnectivity" + classes = ("ajax-modal",) + + +class ChangeCurrentVnfPackage(tables.LinkAction): + name = "change_current_vnf_package" + verbose_name = _("Change Current VNF Package") + url = "horizon:nfv:vnflcm:changevnfpkg" + classes = ("ajax-modal",) + + +class VNFLcmTable(tables.DataTable): + id = tables.Column('id', verbose_name=_("ID"), + link="horizon:nfv:vnflcm:detail",) + vnf_instance_name = tables.Column('vnf_instance_name', + verbose_name=_("VNF Instance Name")) + instantiation_state = tables.Column('instantiation_state', + verbose_name=_("Instantiation State")) + vnf_provider = tables.Column('vnf_provider', + verbose_name=_("VNF Provider")) + vnf_software_version = tables.Column( + 'vnf_software_version', + verbose_name=_("VNF Software Version")) + vnf_product_name = tables.Column('vnf_product_name', + verbose_name=_("VNF Product Name")) + vnfd_id = tables.Column('vnfd_id', verbose_name=_("VNFD ID")) + + class Meta(object): + name = "vnflcm" + verbose_name = _("VNF LCM") + pagination_param = 'vnflcm_marker' + prev_pagination_param = 'prev_vnflcm_marker' + table_actions = (CreateVnfIdentifier, DeleteVnfIdentifier, + MyFilterAction,) + row_actions = (InstantiateVnf, TerminateVnf, DeleteVnfIdentifier, + HealVnf, UpdateVnf, ScaleVnf, + ChangeExternalVnfConnectivity, ChangeCurrentVnfPackage,) diff --git a/tacker_horizon/openstack_dashboard/dashboards/nfv/vnflcm/tabs.py b/tacker_horizon/openstack_dashboard/dashboards/nfv/vnflcm/tabs.py new file mode 100644 index 0000000..e15feeb --- /dev/null +++ b/tacker_horizon/openstack_dashboard/dashboards/nfv/vnflcm/tabs.py @@ -0,0 +1,131 @@ +# Copyright (C) 2024 Fujitsu +# All Rights Reserved. +# +# 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.utils.translation import gettext_lazy as _ + +from horizon import exceptions +from horizon import tabs +from horizon import utils as horizon_utils + +from tacker_horizon.openstack_dashboard import api +from tacker_horizon.openstack_dashboard.dashboards.nfv.vnflcm import tables +from tacker_horizon.openstack_dashboard.dashboards.nfv.vnflcmopocc \ + import tabs as opocc_tab + + +class VNFLcmItem(object): + def __init__(self, vnf_id, vnf_instance_name, instantiation_state, + vnf_provider, vnf_software_version, vnf_product_name, + vnfd_id): + self.id = vnf_id + self.name = vnf_id + self.vnf_instance_name = vnf_instance_name + self.instantiation_state = instantiation_state + self.vnf_provider = vnf_provider + self.vnf_software_version = vnf_software_version + self.vnf_product_name = vnf_product_name + self.vnfd_id = vnfd_id + + +class VNFLcmTab(tabs.TableTab): + name = _("VNFLCM tab") + slug = "vnflcm_tab" + table_classes = (tables.VNFLcmTable,) + template_name = ("horizon/common/_detail_table.html") + preload = False + + def has_prev_data(self, table): + return self._has_prev + + def has_more_data(self, table): + return self._has_more + + def get_vnflcm_data(self): + try: + self._has_prev = False + page_size = horizon_utils.functions.get_page_size(self.request) + marker = self.request.GET.get( + "vnflcm_marker", None) + prev_marker = self.request.GET.get( + "prev_vnflcm_marker", None) + vnf_instances = api.tacker.list_vnf_instances(self.request) + if marker is not None or prev_marker is not None: + for i, instance in enumerate(vnf_instances): + if instance["id"] == marker and i < len(vnf_instances) - 1: + vnf_instances = vnf_instances[i + 1:] + self._has_prev = True + break + if instance["id"] == prev_marker and i > page_size: + vnf_instances = vnf_instances[i - page_size:] + self._has_prev = True + break + + if len(vnf_instances) > page_size: + self._has_more = True + else: + self._has_more = False + + rows = [] + for i, instance in enumerate(vnf_instances): + if i >= page_size: + break + item = VNFLcmItem( + vnf_id=instance.get('id', ''), + vnf_instance_name=instance.get('vnfInstanceName', ''), + instantiation_state=instance.get( + 'instantiationState', ''), + vnf_provider=instance.get('vnfProvider', ''), + vnf_software_version=instance.get( + 'vnfSoftwareVersion', ''), + vnf_product_name=instance.get('vnfProductName', ''), + vnfd_id=instance.get('vnfdId', '')) + rows.append(item) + return rows + except Exception: + self._has_more = False + error_message = _('Failed to get VNF Instances.') + exceptions.handle(self.request, error_message) + + return [] + + +class VNFLcmTabs(tabs.TabGroup): + slug = "vnflcm_tabs" + tabs = (VNFLcmTab,) + sticky = True + + +class VNFLcmOpOccItem(object): + def __init__(self, id, operation_state, vnf_instance_id, operation): + self.id = id + self.operation_state = operation_state + self.vnf_instance_id = vnf_instance_id + self.operation = operation + + +class VNFLcmDetailTab(tabs.Tab): + name = _("VNF Instance Detail Tab") + slug = "vnflcm_detail_tab" + template_name = "nfv/vnflcm/vnflcm_detail.html" + + def get_context_data(self, request): + return {'vnflcm': self.tab_group.kwargs['vnflcm']} + + +class VNFLcmDetailTabs(tabs.TabGroup): + slug = "vnflcm_detail_tabs" + tabs = (VNFLcmDetailTab, opocc_tab.VNFLcmOpOccTab,) + sticky = True diff --git a/tacker_horizon/openstack_dashboard/dashboards/nfv/vnflcm/templates/vnflcm/_change_connectivity.html b/tacker_horizon/openstack_dashboard/dashboards/nfv/vnflcm/templates/vnflcm/_change_connectivity.html new file mode 100644 index 0000000..bf925be --- /dev/null +++ b/tacker_horizon/openstack_dashboard/dashboards/nfv/vnflcm/templates/vnflcm/_change_connectivity.html @@ -0,0 +1,9 @@ +{% extends "horizon/common/_modal_form.html" %} +{% load i18n %} + +{% block form_attrs %}enctype="multipart/form-data"{% endblock %} + +{% block modal-body-right %} +

{% trans "Description:" %}

+

{% trans "Change a External VNF Connectivity." %}

+{% endblock %} diff --git a/tacker_horizon/openstack_dashboard/dashboards/nfv/vnflcm/templates/vnflcm/_change_vnfpkg.html b/tacker_horizon/openstack_dashboard/dashboards/nfv/vnflcm/templates/vnflcm/_change_vnfpkg.html new file mode 100644 index 0000000..3def993 --- /dev/null +++ b/tacker_horizon/openstack_dashboard/dashboards/nfv/vnflcm/templates/vnflcm/_change_vnfpkg.html @@ -0,0 +1,9 @@ +{% extends "horizon/common/_modal_form.html" %} +{% load i18n %} + +{% block form_attrs %}enctype="multipart/form-data"{% endblock %} + +{% block modal-body-right %} +

{% trans "Description:" %}

+

{% trans "Change a Current VNF Package." %}

+{% endblock %} diff --git a/tacker_horizon/openstack_dashboard/dashboards/nfv/vnflcm/templates/vnflcm/_create_vnf_identifier.html b/tacker_horizon/openstack_dashboard/dashboards/nfv/vnflcm/templates/vnflcm/_create_vnf_identifier.html new file mode 100644 index 0000000..fd4d7b9 --- /dev/null +++ b/tacker_horizon/openstack_dashboard/dashboards/nfv/vnflcm/templates/vnflcm/_create_vnf_identifier.html @@ -0,0 +1,9 @@ +{% extends "horizon/common/_modal_form.html" %} +{% load i18n %} + +{% block form_attrs %}enctype="multipart/form-data"{% endblock %} + +{% block modal-body-right %} +

{% trans "Description:" %}

+

{% trans "Create a VNF Identifier." %}

+{% endblock %} diff --git a/tacker_horizon/openstack_dashboard/dashboards/nfv/vnflcm/templates/vnflcm/_heal_vnf.html b/tacker_horizon/openstack_dashboard/dashboards/nfv/vnflcm/templates/vnflcm/_heal_vnf.html new file mode 100644 index 0000000..ee74aab --- /dev/null +++ b/tacker_horizon/openstack_dashboard/dashboards/nfv/vnflcm/templates/vnflcm/_heal_vnf.html @@ -0,0 +1,9 @@ +{% extends "horizon/common/_modal_form.html" %} +{% load i18n %} + +{% block form_attrs %}enctype="multipart/form-data"{% endblock %} + +{% block modal-body-right %} +

{% trans "Description:" %}

+

{% trans "Heal a VNF instance." %}

+{% endblock %} diff --git a/tacker_horizon/openstack_dashboard/dashboards/nfv/vnflcm/templates/vnflcm/_instantiate_vnf.html b/tacker_horizon/openstack_dashboard/dashboards/nfv/vnflcm/templates/vnflcm/_instantiate_vnf.html new file mode 100644 index 0000000..aba5cc3 --- /dev/null +++ b/tacker_horizon/openstack_dashboard/dashboards/nfv/vnflcm/templates/vnflcm/_instantiate_vnf.html @@ -0,0 +1,9 @@ +{% extends "horizon/common/_modal_form.html" %} +{% load i18n %} + +{% block form_attrs %}enctype="multipart/form-data"{% endblock %} + +{% block modal-body-right %} +

{% trans "Description:" %}

+

{% trans "Instantiate a VNF instance." %}

+{% endblock %} diff --git a/tacker_horizon/openstack_dashboard/dashboards/nfv/vnflcm/templates/vnflcm/_scale_vnf.html b/tacker_horizon/openstack_dashboard/dashboards/nfv/vnflcm/templates/vnflcm/_scale_vnf.html new file mode 100644 index 0000000..a939d9b --- /dev/null +++ b/tacker_horizon/openstack_dashboard/dashboards/nfv/vnflcm/templates/vnflcm/_scale_vnf.html @@ -0,0 +1,9 @@ +{% extends "horizon/common/_modal_form.html" %} +{% load i18n %} + +{% block form_attrs %}enctype="multipart/form-data"{% endblock %} + +{% block modal-body-right %} +

{% trans "Description:" %}

+

{% trans "Scale a VNF instance." %}

+{% endblock %} diff --git a/tacker_horizon/openstack_dashboard/dashboards/nfv/vnflcm/templates/vnflcm/_terminate_vnf.html b/tacker_horizon/openstack_dashboard/dashboards/nfv/vnflcm/templates/vnflcm/_terminate_vnf.html new file mode 100644 index 0000000..df7876f --- /dev/null +++ b/tacker_horizon/openstack_dashboard/dashboards/nfv/vnflcm/templates/vnflcm/_terminate_vnf.html @@ -0,0 +1,9 @@ +{% extends "horizon/common/_modal_form.html" %} +{% load i18n %} + +{% block form_attrs %}enctype="multipart/form-data"{% endblock %} + +{% block modal-body-right %} +

{% trans "Description:" %}

+

{% trans "Terminate a VNF instance." %}

+{% endblock %} diff --git a/tacker_horizon/openstack_dashboard/dashboards/nfv/vnflcm/templates/vnflcm/_update_vnf.html b/tacker_horizon/openstack_dashboard/dashboards/nfv/vnflcm/templates/vnflcm/_update_vnf.html new file mode 100644 index 0000000..2bcdb58 --- /dev/null +++ b/tacker_horizon/openstack_dashboard/dashboards/nfv/vnflcm/templates/vnflcm/_update_vnf.html @@ -0,0 +1,9 @@ +{% extends "horizon/common/_modal_form.html" %} +{% load i18n %} + +{% block form_attrs %}enctype="multipart/form-data"{% endblock %} + +{% block modal-body-right %} +

{% trans "Description:" %}

+

{% trans "Update a VNF instance." %}

+{% endblock %} diff --git a/tacker_horizon/openstack_dashboard/dashboards/nfv/vnflcm/templates/vnflcm/change_connectivity.html b/tacker_horizon/openstack_dashboard/dashboards/nfv/vnflcm/templates/vnflcm/change_connectivity.html new file mode 100644 index 0000000..f60a13d --- /dev/null +++ b/tacker_horizon/openstack_dashboard/dashboards/nfv/vnflcm/templates/vnflcm/change_connectivity.html @@ -0,0 +1,11 @@ +{% extends 'base.html' %} +{% load i18n %} +{% block title %}{% trans "Change External VNF Connectivity" %}{% endblock %} + +{% block page_header %} + {% include "horizon/common/_page_header.html" with title=_("Change External VNF Connectivity") %} +{% endblock page_header %} + +{% block main %} + {% include 'nfv/vnflcm/_change_connectivity.html' %} +{% endblock %} diff --git a/tacker_horizon/openstack_dashboard/dashboards/nfv/vnflcm/templates/vnflcm/change_vnfpkg.html b/tacker_horizon/openstack_dashboard/dashboards/nfv/vnflcm/templates/vnflcm/change_vnfpkg.html new file mode 100644 index 0000000..ff3e58a --- /dev/null +++ b/tacker_horizon/openstack_dashboard/dashboards/nfv/vnflcm/templates/vnflcm/change_vnfpkg.html @@ -0,0 +1,11 @@ +{% extends 'base.html' %} +{% load i18n %} +{% block title %}{% trans "Change Current VNF Package" %}{% endblock %} + +{% block page_header %} + {% include "horizon/common/_page_header.html" with title=_("Change Current VNF Package") %} +{% endblock page_header %} + +{% block main %} + {% include 'nfv/vnflcm/_change_vnfpkg.html' %} +{% endblock %} diff --git a/tacker_horizon/openstack_dashboard/dashboards/nfv/vnflcm/templates/vnflcm/create_vnf_identifier.html b/tacker_horizon/openstack_dashboard/dashboards/nfv/vnflcm/templates/vnflcm/create_vnf_identifier.html new file mode 100644 index 0000000..8eed33f --- /dev/null +++ b/tacker_horizon/openstack_dashboard/dashboards/nfv/vnflcm/templates/vnflcm/create_vnf_identifier.html @@ -0,0 +1,11 @@ +{% extends 'base.html' %} +{% load i18n %} +{% block title %}{% trans "create_vnf_id" %}{% endblock %} + +{% block page_header %} + {% include "horizon/common/_page_header.html" with title=_("Create VNF Identifier") %} +{% endblock page_header %} + +{% block main %} + {% include 'nfv/vnflcm/_create_vnf_identifier.html' %} +{% endblock %} diff --git a/tacker_horizon/openstack_dashboard/dashboards/nfv/vnflcm/templates/vnflcm/detail.html b/tacker_horizon/openstack_dashboard/dashboards/nfv/vnflcm/templates/vnflcm/detail.html new file mode 100644 index 0000000..252ea29 --- /dev/null +++ b/tacker_horizon/openstack_dashboard/dashboards/nfv/vnflcm/templates/vnflcm/detail.html @@ -0,0 +1,15 @@ +{% extends 'base.html' %} +{% load i18n %} +{% block title %}{% trans "VNF LCM Detail" %}{% endblock %} + +{% block page_header %} + {% include "horizon/common/_page_header.html" with title=page_title %} +{% endblock page_header %} + +{% block main %} +
+
+ {{ tab_group.render }} +
+
+{% endblock %} diff --git a/tacker_horizon/openstack_dashboard/dashboards/nfv/vnflcm/templates/vnflcm/heal_vnf.html b/tacker_horizon/openstack_dashboard/dashboards/nfv/vnflcm/templates/vnflcm/heal_vnf.html new file mode 100644 index 0000000..daa2b7c --- /dev/null +++ b/tacker_horizon/openstack_dashboard/dashboards/nfv/vnflcm/templates/vnflcm/heal_vnf.html @@ -0,0 +1,11 @@ +{% extends 'base.html' %} +{% load i18n %} +{% block title %}{% trans "Heal VNF" %}{% endblock %} + +{% block page_header %} + {% include "horizon/common/_page_header.html" with title=_("Heal VNF") %} +{% endblock page_header %} + +{% block main %} + {% include 'nfv/vnflcm/_heal_vnf.html' %} +{% endblock %} diff --git a/tacker_horizon/openstack_dashboard/dashboards/nfv/vnflcm/templates/vnflcm/index.html b/tacker_horizon/openstack_dashboard/dashboards/nfv/vnflcm/templates/vnflcm/index.html new file mode 100644 index 0000000..4979767 --- /dev/null +++ b/tacker_horizon/openstack_dashboard/dashboards/nfv/vnflcm/templates/vnflcm/index.html @@ -0,0 +1,15 @@ +{% extends 'base.html' %} +{% load i18n %} +{% block title %}{% trans "VNF LCM" %}{% endblock %} + +{% block page_header %} + {% include "horizon/common/_page_header.html" with title=_("VNF LCM") %} +{% endblock page_header %} + +{% block main %} +
+
+ {{ tab_group.render }} +
+
+{% endblock %} diff --git a/tacker_horizon/openstack_dashboard/dashboards/nfv/vnflcm/templates/vnflcm/instantiate_vnf.html b/tacker_horizon/openstack_dashboard/dashboards/nfv/vnflcm/templates/vnflcm/instantiate_vnf.html new file mode 100644 index 0000000..c4faf57 --- /dev/null +++ b/tacker_horizon/openstack_dashboard/dashboards/nfv/vnflcm/templates/vnflcm/instantiate_vnf.html @@ -0,0 +1,11 @@ +{% extends 'base.html' %} +{% load i18n %} +{% block title %}{% trans "Instantiate VNF" %}{% endblock %} + +{% block page_header %} + {% include "horizon/common/_page_header.html" with title=_("Instantiate VNF") %} +{% endblock page_header %} + +{% block main %} + {% include 'nfv/vnflcm/_instantiate_vnf.html' %} +{% endblock %} diff --git a/tacker_horizon/openstack_dashboard/dashboards/nfv/vnflcm/templates/vnflcm/scale_vnf.html b/tacker_horizon/openstack_dashboard/dashboards/nfv/vnflcm/templates/vnflcm/scale_vnf.html new file mode 100644 index 0000000..8830964 --- /dev/null +++ b/tacker_horizon/openstack_dashboard/dashboards/nfv/vnflcm/templates/vnflcm/scale_vnf.html @@ -0,0 +1,11 @@ +{% extends 'base.html' %} +{% load i18n %} +{% block title %}{% trans "Scale VNF" %}{% endblock %} + +{% block page_header %} + {% include "horizon/common/_page_header.html" with title=_("Scale VNF") %} +{% endblock page_header %} + +{% block main %} + {% include 'nfv/vnflcm/_scale_vnf.html' %} +{% endblock %} diff --git a/tacker_horizon/openstack_dashboard/dashboards/nfv/vnflcm/templates/vnflcm/terminate_vnf.html b/tacker_horizon/openstack_dashboard/dashboards/nfv/vnflcm/templates/vnflcm/terminate_vnf.html new file mode 100644 index 0000000..528dd65 --- /dev/null +++ b/tacker_horizon/openstack_dashboard/dashboards/nfv/vnflcm/templates/vnflcm/terminate_vnf.html @@ -0,0 +1,11 @@ +{% extends 'base.html' %} +{% load i18n %} +{% block title %}{% trans "Terminate VNF" %}{% endblock %} + +{% block page_header %} + {% include "horizon/common/_page_header.html" with title=_("Terminate VNF") %} +{% endblock page_header %} + +{% block main %} + {% include 'nfv/vnflcm/_terminate_vnf.html' %} +{% endblock %} diff --git a/tacker_horizon/openstack_dashboard/dashboards/nfv/vnflcm/templates/vnflcm/update_vnf.html b/tacker_horizon/openstack_dashboard/dashboards/nfv/vnflcm/templates/vnflcm/update_vnf.html new file mode 100644 index 0000000..5e42efe --- /dev/null +++ b/tacker_horizon/openstack_dashboard/dashboards/nfv/vnflcm/templates/vnflcm/update_vnf.html @@ -0,0 +1,11 @@ +{% extends 'base.html' %} +{% load i18n %} +{% block title %}{% trans "Update VNF" %}{% endblock %} + +{% block page_header %} + {% include "horizon/common/_page_header.html" with title=_("Update VNF") %} +{% endblock page_header %} + +{% block main %} + {% include 'nfv/vnflcm/_update_vnf.html' %} +{% endblock %} diff --git a/tacker_horizon/openstack_dashboard/dashboards/nfv/vnflcm/templates/vnflcm/vnflcm_detail.html b/tacker_horizon/openstack_dashboard/dashboards/nfv/vnflcm/templates/vnflcm/vnflcm_detail.html new file mode 100644 index 0000000..dfcaff3 --- /dev/null +++ b/tacker_horizon/openstack_dashboard/dashboards/nfv/vnflcm/templates/vnflcm/vnflcm_detail.html @@ -0,0 +1,30 @@ +{% load i18n %} + +
+
+
{% trans "ID" %}
+
{{ vnflcm.id }}
+
{% trans "Instantiated Vnf Info" %}
+
{{ vnflcm.instantiatedVnfInfo }}
+
{% trans "Instantiation State" %}
+
{{ vnflcm.instantiationState }}
+
{% trans "Links" %}
+
{{ vnflcm.links }}
+
{% trans "VIM Connection Info" %}
+
{{ vnflcm.vimConnectionInfo }}
+
{% trans "VNF Instance Description" %}
+
{{ vnflcm.vnfInstanceDescription }}
+
{% trans "VNF Instance Name" %}
+
{{ vnflcm.vnfInstanceName }}
+
{% trans "VNF Product Name" %}
+
{{ vnflcm.vnfProductName }}
+
{% trans "VNF Provider" %}
+
{{ vnflcm.vnfProvider }}
+
{% trans "VNF Software Version" %}
+
{{ vnflcm.vnfSoftwareVersion }}
+
{% trans "VNFD ID" %}
+
{{ vnflcm.vnfdId }}
+
{% trans "VNFD Version" %}
+
{{ vnflcm.vnfdVersion }}
+
+
diff --git a/tacker_horizon/openstack_dashboard/dashboards/nfv/vnflcm/urls.py b/tacker_horizon/openstack_dashboard/dashboards/nfv/vnflcm/urls.py new file mode 100644 index 0000000..328cecf --- /dev/null +++ b/tacker_horizon/openstack_dashboard/dashboards/nfv/vnflcm/urls.py @@ -0,0 +1,44 @@ +# Copyright (C) 2024 Fujitsu +# All Rights Reserved. +# +# 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.urls import re_path + +from tacker_horizon.openstack_dashboard.dashboards.nfv.vnflcm \ + import views + +urlpatterns = [ + re_path(r'^$', views.IndexView.as_view(), name='index'), + re_path(r'^(?P[^/]+)/$', views.DetailView.as_view(), name='detail'), + re_path(r'^createvnfidentifier$', views.CreateVnfIdentifierView.as_view(), + name='createvnfidentifier'), + re_path(r'^(?P[^/]+)/instantiatevnf/$', + views.InstantiateVnfView.as_view(), + name='instantiatevnf'), + re_path(r'^(?P[^/]+)/terminatevnf/$', views.TerminateVnfView.as_view(), + name='terminatevnf'), + re_path(r'^(?P[^/]+)/healvnf/$', views.HealVnfView.as_view(), + name='healvnf'), + re_path(r'^(?P[^/]+)/updatevnf/$', views.UpdateVnfView.as_view(), + name='updatevnf'), + re_path(r'^(?P[^/]+)/scalevnf/$', views.ScaleVnfView.as_view(), + name='scalevnf'), + re_path(r'^(?P[^/]+)/changeconnectivity/$', + views.ChangeExternalVnfConnectivityView.as_view(), + name='changeconnectivity'), + re_path(r'^(?P[^/]+)/changevnfpkg/$', + views.ChangeCurrentVnfPackageView.as_view(), + name='changevnfpkg'), +] diff --git a/tacker_horizon/openstack_dashboard/dashboards/nfv/vnflcm/views.py b/tacker_horizon/openstack_dashboard/dashboards/nfv/vnflcm/views.py new file mode 100644 index 0000000..273aa5b --- /dev/null +++ b/tacker_horizon/openstack_dashboard/dashboards/nfv/vnflcm/views.py @@ -0,0 +1,200 @@ +# Copyright (C) 2024 Fujitsu +# All Rights Reserved. +# +# 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.urls import reverse +from django.urls import reverse_lazy +from django.utils.translation import gettext_lazy as _ + +from horizon import exceptions +from horizon import forms +from horizon import tabs +from horizon.utils import memoized + +from tacker_horizon.openstack_dashboard import api as tacker_api +from tacker_horizon.openstack_dashboard.dashboards.nfv.vnflcm \ + import forms as project_forms +from tacker_horizon.openstack_dashboard.dashboards.nfv.vnflcm \ + import tabs as vnflcm_tabs + + +class IndexView(tabs.TabbedTableView): + tab_group_class = vnflcm_tabs.VNFLcmTabs + template_name = 'nfv/vnflcm/index.html' + + +class CreateVnfIdentifierView(forms.ModalFormView): + form_class = project_forms.CreateVnfIdentifier + template_name = 'nfv/vnflcm/create_vnf_identifier.html' + success_url = reverse_lazy("horizon:nfv:vnflcm:index") + modal_id = "add_service_modal" + modal_header = _("Create VNF Identifier") + submit_label = _("Create VNF Identifier") + submit_url = "horizon:nfv:vnflcm:createvnfidentifier" + + def get_context_data(self, **kwargs): + context = super( + CreateVnfIdentifierView, self).get_context_data(**kwargs) + context['submit_url'] = reverse(self.submit_url) + return context + + +class InstantiateVnfView(forms.ModalFormView): + form_class = project_forms.InstantiateVnf + template_name = 'nfv/vnflcm/instantiate_vnf.html' + success_url = reverse_lazy("horizon:nfv:vnflcm:index") + modal_id = "add_service_modal" + modal_header = _("Instantiate VNF") + submit_label = _("Instantiate VNF") + submit_url = "horizon:nfv:vnflcm:instantiatevnf" + + def get_context_data(self, **kwargs): + context = super(InstantiateVnfView, self).get_context_data(**kwargs) + context['submit_url'] = reverse(self.submit_url, + kwargs={'id': self.kwargs['id']}) + return context + + +class TerminateVnfView(forms.ModalFormView): + form_class = project_forms.TerminateVnf + template_name = 'nfv/vnflcm/terminate_vnf.html' + success_url = reverse_lazy("horizon:nfv:vnflcm:index") + modal_id = "add_service_modal" + modal_header = _("Terminate VNF") + submit_label = _("Terminate VNF") + submit_url = "horizon:nfv:vnflcm:terminatevnf" + + def get_context_data(self, **kwargs): + context = super(TerminateVnfView, self).get_context_data(**kwargs) + context['submit_url'] = reverse(self.submit_url, + kwargs={'id': self.kwargs['id']}) + return context + + +class HealVnfView(forms.ModalFormView): + form_class = project_forms.HealVnf + template_name = 'nfv/vnflcm/heal_vnf.html' + success_url = reverse_lazy("horizon:nfv:vnflcm:index") + modal_id = "add_service_modal" + modal_header = _("Heal VNF") + submit_label = _("Heal VNF") + submit_url = "horizon:nfv:vnflcm:healvnf" + + def get_context_data(self, **kwargs): + context = super(HealVnfView, self).get_context_data(**kwargs) + context['submit_url'] = reverse(self.submit_url, + kwargs={'id': self.kwargs['id']}) + return context + + +class UpdateVnfView(forms.ModalFormView): + form_class = project_forms.UpdateVnf + template_name = 'nfv/vnflcm/update_vnf.html' + success_url = reverse_lazy("horizon:nfv:vnflcm:index") + modal_id = "add_service_modal" + modal_header = _("Update VNF") + submit_label = _("Update VNF") + submit_url = "horizon:nfv:vnflcm:updatevnf" + + def get_context_data(self, **kwargs): + context = super(UpdateVnfView, self).get_context_data(**kwargs) + context['submit_url'] = reverse(self.submit_url, + kwargs={'id': self.kwargs['id']}) + return context + + +class ScaleVnfView(forms.ModalFormView): + form_class = project_forms.ScaleVnf + template_name = 'nfv/vnflcm/scale_vnf.html' + success_url = reverse_lazy("horizon:nfv:vnflcm:index") + modal_id = "add_service_modal" + modal_header = _("Scale VNF") + submit_label = _("Scale VNF") + submit_url = "horizon:nfv:vnflcm:scalevnf" + + def get_context_data(self, **kwargs): + context = super(ScaleVnfView, self).get_context_data(**kwargs) + context['submit_url'] = reverse(self.submit_url, + kwargs={'id': self.kwargs['id']}) + return context + + +class ChangeExternalVnfConnectivityView(forms.ModalFormView): + form_class = project_forms.ChangeExternalVnfConnectivity + template_name = 'nfv/vnflcm/change_connectivity.html' + success_url = reverse_lazy("horizon:nfv:vnflcm:index") + modal_id = "add_service_modal" + modal_header = _("Change External VNF Connectivity") + submit_label = _("Change External VNF Connectivity") + submit_url = "horizon:nfv:vnflcm:changeconnectivity" + + def get_context_data(self, **kwargs): + context = super( + ChangeExternalVnfConnectivityView, self).get_context_data(**kwargs) + context['submit_url'] = reverse(self.submit_url, + kwargs={'id': self.kwargs['id']}) + return context + + +class ChangeCurrentVnfPackageView(forms.ModalFormView): + form_class = project_forms.ChangeCurrentVnfPackage + template_name = 'nfv/vnflcm/change_vnfpkg.html' + success_url = reverse_lazy("horizon:nfv:vnflcm:index") + modal_id = "add_service_modal" + modal_header = _("Change Current VNF Package") + submit_label = _("Change Current VNF Package") + submit_url = "horizon:nfv:vnflcm:changevnfpkg" + + def get_context_data(self, **kwargs): + context = super( + ChangeCurrentVnfPackageView, self).get_context_data(**kwargs) + context['submit_url'] = reverse(self.submit_url, + kwargs={'id': self.kwargs['id']}) + return context + + +class DetailView(tabs.TabbedTableView): + tab_group_class = vnflcm_tabs.VNFLcmDetailTabs + template_name = 'nfv/vnflcm/detail.html' + redirect_url = 'horizon:nfv:vnflcm:index' + page_title = _("VNF Instance Detail: {{ id }}") + + def get_context_data(self, **kwargs): + context = super(DetailView, self).get_context_data(**kwargs) + vnflcm = self.get_data() + context['vnflcm'] = vnflcm + context['id'] = kwargs['id'] + context['url'] = reverse(self.redirect_url) + return context + + @memoized.memoized_method + def get_data(self): + vnf_id = self.kwargs['id'] + + try: + vnflcm = tacker_api.tacker.get_vnf_instance(self.request, vnf_id) + vnflcm['links'] = vnflcm.get('_links', '') + return vnflcm + except Exception: + redirect = reverse(self.redirect_url) + exceptions.handle( + self.request, + _('Failed to get VNF instance. (id: %s)') % vnf_id, + redirect=redirect) + raise exceptions.Http302(redirect) + + def get_tabs(self, request, *args, **kwargs): + vnflcm = self.get_data() + return self.tab_group_class(request, vnflcm=vnflcm, **kwargs) diff --git a/tacker_horizon/openstack_dashboard/dashboards/nfv/vnflcmopocc/__init__.py b/tacker_horizon/openstack_dashboard/dashboards/nfv/vnflcmopocc/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tacker_horizon/openstack_dashboard/dashboards/nfv/vnflcmopocc/panel.py b/tacker_horizon/openstack_dashboard/dashboards/nfv/vnflcmopocc/panel.py new file mode 100644 index 0000000..501f578 --- /dev/null +++ b/tacker_horizon/openstack_dashboard/dashboards/nfv/vnflcmopocc/panel.py @@ -0,0 +1,29 @@ +# Copyright (C) 2024 Fujitsu +# All Rights Reserved. +# +# 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.utils.translation import gettext_lazy as _ + +import horizon + +from tacker_horizon.openstack_dashboard.dashboards.nfv import dashboard + + +class Vnfopocc(horizon.Panel): + name = _("VNF OP OCC") + slug = "vnflcmopocc" + + +dashboard.Nfv.register(Vnfopocc) diff --git a/tacker_horizon/openstack_dashboard/dashboards/nfv/vnflcmopocc/tables.py b/tacker_horizon/openstack_dashboard/dashboards/nfv/vnflcmopocc/tables.py new file mode 100644 index 0000000..0587072 --- /dev/null +++ b/tacker_horizon/openstack_dashboard/dashboards/nfv/vnflcmopocc/tables.py @@ -0,0 +1,118 @@ +# Copyright (C) 2024 Fujitsu +# All Rights Reserved. +# +# 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.utils.translation import gettext_lazy as _ +from django.utils.translation import ngettext_lazy + +from horizon import tables + +from tacker_horizon.openstack_dashboard import api + + +class MyFilterAction(tables.FilterAction): + name = "myfilter" + + +class VnfLcmRollback(tables.BatchAction): + name = "vnflcmrollback" + verbose_name = _("Rollback VNF Lifecycle Management Operation") + + @staticmethod + def action_present(count): + return ngettext_lazy( + "Rollback VNF Lifecycle Management Operation", + "Rollback VNF Lifecycle Management Operation", + count + ) + + @staticmethod + def action_past(count): + return ngettext_lazy( + "Rollback VNF Lifecycle Management Operation", + "Rollback VNF Lifecycle Management Operation", + count + ) + + def action(self, request, obj_id): + api.tacker.rollback_vnf_lcm_op_occ(request, obj_id) + + +class VnfLcmRetry(tables.BatchAction): + name = "vnflcmretry" + verbose_name = _("Retry") + + @staticmethod + def action_present(count): + return ngettext_lazy( + "Retry", + "Retry", + count + ) + + @staticmethod + def action_past(count): + return ngettext_lazy( + "Retry", + "Retry", + count + ) + + def action(self, request, obj_id): + api.tacker.retry_vnf_lcm_op_occ(request, obj_id) + + +class VnfLcmFail(tables.BatchAction): + name = "vnflcmfail" + verbose_name = _("Fail") + + @staticmethod + def action_present(count): + return ngettext_lazy( + "Fail", + "Fail", + count + ) + + @staticmethod + def action_past(count): + return ngettext_lazy( + "Fail", + "Fail", + count + ) + + def action(self, request, obj_id): + api.tacker.fail_vnf_lcm_op_occ(request, obj_id) + + +class VNFLcmOpOccTable(tables.DataTable): + id = tables.Column('id', verbose_name=_("ID"), + link="horizon:nfv:vnflcmopocc:detail",) + operation_state = tables.Column('operation_state', + verbose_name=_("OperationState")) + vnf_instance_id = tables.Column('vnf_instance_id', + verbose_name=_("VNFInstanceID")) + operation = tables.Column('operation', verbose_name=_("Operation")) + + class Meta(object): + name = "vnflcmopocc" + verbose_name = _("VNF LCM OP OCC") + pagination_param = 'opocc_marker' + prev_pagination_param = 'prev_opocc_marker' + table_actions = (VnfLcmRollback, VnfLcmRetry, VnfLcmFail, + MyFilterAction,) + row_actions = (VnfLcmRollback, VnfLcmRetry, VnfLcmFail,) + multi_select = True diff --git a/tacker_horizon/openstack_dashboard/dashboards/nfv/vnflcmopocc/tabs.py b/tacker_horizon/openstack_dashboard/dashboards/nfv/vnflcmopocc/tabs.py new file mode 100644 index 0000000..42dd61b --- /dev/null +++ b/tacker_horizon/openstack_dashboard/dashboards/nfv/vnflcmopocc/tabs.py @@ -0,0 +1,118 @@ +# Copyright (C) 2024 Fujitsu +# All Rights Reserved. +# +# 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.utils.translation import gettext_lazy as _ + +from horizon import exceptions +from horizon import tabs +from horizon import utils as horizon_utils + +from tacker_horizon.openstack_dashboard import api +from tacker_horizon.openstack_dashboard.dashboards.nfv.vnflcmopocc \ + import tables + + +class VNFLcmOpOccItem(object): + def __init__(self, opocc_id, operation_state, vnf_instance_id, + operation): + self.id = opocc_id + self.name = opocc_id + self.operation_state = operation_state + self.vnf_instance_id = vnf_instance_id + self.operation = operation + + +class VNFLcmOpOccTab(tabs.TableTab): + name = _("List LCM Operation Occurrences") + slug = "vnflcmopocc_tab" + table_classes = (tables.VNFLcmOpOccTable,) + template_name = ("horizon/common/_detail_table.html") + preload = False + + def has_prev_data(self, table): + return self._has_prev + + def has_more_data(self, table): + return self._has_more + + def get_vnflcmopocc_data(self): + try: + opoccs = [] + self._has_prev = False + page_size = horizon_utils.functions.get_page_size(self.request) + marker = self.request.GET.get( + "opocc_marker", None) + prev_marker = self.request.GET.get( + "prev_opocc_marker", None) + params = {} + opocc_id = self.request.resolver_match.kwargs.get("id", None) + if opocc_id: + params["filter"] = "(eq,vnfInstanceId,{0})".format(opocc_id) + opoccs = api.tacker.list_vnf_lcm_op_occs(self.request, **params) + if marker is not None or prev_marker is not None: + for i, opocc in enumerate(opoccs): + if opocc["id"] == marker and i < len(opoccs) - 1: + opoccs = opoccs[i + 1:] + self._has_prev = True + break + if opocc["id"] == prev_marker and i > page_size: + opoccs = opoccs[i - page_size:] + self._has_prev = True + break + + if len(opoccs) > page_size: + self._has_more = True + else: + self._has_more = False + + rows = [] + for i, opocc in enumerate(opoccs): + if i >= page_size: + break + item = VNFLcmOpOccItem( + opocc_id=opocc.get('id', ''), + operation_state=opocc.get('operationState', ''), + vnf_instance_id=opocc.get('vnfInstanceId', ''), + operation=opocc.get('operation', '')) + rows.append(item) + return rows + except Exception: + self._has_more = False + error_message = _('Failed to get VNF LCM operation occurrences.') + exceptions.handle(self.request, error_message) + + return [] + + +class VNFLcmOpOccTabs(tabs.TabGroup): + slug = "vnflcmopocc_tabs" + tabs = (VNFLcmOpOccTab,) + sticky = True + + +class VNFOpOccDetailTab(tabs.Tab): + name = _("LCM OP OCC Detail") + slug = "vnflcmopocc_detail_tab" + template_name = "nfv/vnflcmopocc/vnflcmopocc_detail.html" + + def get_context_data(self, request): + return {'vnflcmopocc': self.tab_group.kwargs['vnflcmopocc']} + + +class VNFOpOccDetailTabs(tabs.TabGroup): + slug = "vnflcmopocc_detail_tabs" + tabs = (VNFOpOccDetailTab,) + sticky = True diff --git a/tacker_horizon/openstack_dashboard/dashboards/nfv/vnflcmopocc/templates/vnflcmopocc/detail.html b/tacker_horizon/openstack_dashboard/dashboards/nfv/vnflcmopocc/templates/vnflcmopocc/detail.html new file mode 100644 index 0000000..6e8100a --- /dev/null +++ b/tacker_horizon/openstack_dashboard/dashboards/nfv/vnflcmopocc/templates/vnflcmopocc/detail.html @@ -0,0 +1,15 @@ +{% extends 'base.html' %} +{% load i18n %} +{% block title %}{% trans "VNF LCM OP OCC" %}{% endblock %} + +{% block page_header %} + {% include "horizon/common/_page_header.html" with title=page_title %} +{% endblock page_header %} + +{% block main %} +
+
+ {{ tab_group.render }} +
+
+{% endblock %} diff --git a/tacker_horizon/openstack_dashboard/dashboards/nfv/vnflcmopocc/templates/vnflcmopocc/index.html b/tacker_horizon/openstack_dashboard/dashboards/nfv/vnflcmopocc/templates/vnflcmopocc/index.html new file mode 100644 index 0000000..5a4fad4 --- /dev/null +++ b/tacker_horizon/openstack_dashboard/dashboards/nfv/vnflcmopocc/templates/vnflcmopocc/index.html @@ -0,0 +1,15 @@ +{% extends 'base.html' %} +{% load i18n %} +{% block title %}{% trans "VNF LCM OP OCC" %}{% endblock %} + +{% block page_header %} + {% include "horizon/common/_page_header.html" with title=_("VNF LCM OP OCC") %} +{% endblock page_header %} + +{% block main %} +
+
+ {{ tab_group.render }} +
+
+{% endblock %} diff --git a/tacker_horizon/openstack_dashboard/dashboards/nfv/vnflcmopocc/templates/vnflcmopocc/vnflcmopocc_detail.html b/tacker_horizon/openstack_dashboard/dashboards/nfv/vnflcmopocc/templates/vnflcmopocc/vnflcmopocc_detail.html new file mode 100644 index 0000000..eb9d129 --- /dev/null +++ b/tacker_horizon/openstack_dashboard/dashboards/nfv/vnflcmopocc/templates/vnflcmopocc/vnflcmopocc_detail.html @@ -0,0 +1,38 @@ +{% load i18n %} + +
+
+
{% trans "CancelMode" %}
+
{{ vnflcmopocc.cancelMode }}
+
{% trans "ChangedExtConnectivity" %}
+
{{ vnflcmopocc.changedExtConnectivity }}
+
{% trans "ChangedInfo" %}
+
{{ vnflcmopocc.changedInfo }}
+
{% trans "Error" %}
+
{{ vnflcmopocc.error }}
+
{% trans "GrantID" %}
+
{{ vnflcmopocc.grantId }}
+
{% trans "ID" %}
+
{{ vnflcmopocc.id }}
+
{% trans "IsAutomaticInvocation" %}
+
{{ vnflcmopocc.isAutomaticInvocation }}
+
{% trans "IsCancelPending" %}
+
{{ vnflcmopocc.isCancelPending }}
+
{% trans "Links" %}
+
{{ vnflcmopocc.links }}
+
{% trans "Operation" %}
+
{{ vnflcmopocc.operation }}
+
{% trans "OperationParams" %}
+
{{ vnflcmopocc.operationParams }}
+
{% trans "OperationState" %}
+
{{ vnflcmopocc.operationState }}
+
{% trans "ResourceChanges" %}
+
{{ vnflcmopocc.resourceChanges }}
+
{% trans "StartTime" %}
+
{{ vnflcmopocc.startTime }}
+
{% trans "StateEnteredTime" %}
+
{{ vnflcmopocc.stateEnteredTime }}
+
{% trans "VnfInstanceID" %}
+
{{ vnflcmopocc.vnfInstanceId }}
+
+
diff --git a/tacker_horizon/openstack_dashboard/dashboards/nfv/vnflcmopocc/urls.py b/tacker_horizon/openstack_dashboard/dashboards/nfv/vnflcmopocc/urls.py new file mode 100644 index 0000000..1153543 --- /dev/null +++ b/tacker_horizon/openstack_dashboard/dashboards/nfv/vnflcmopocc/urls.py @@ -0,0 +1,25 @@ +# Copyright (C) 2024 Fujitsu +# All Rights Reserved. +# +# 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.urls import re_path + +from tacker_horizon.openstack_dashboard.dashboards.nfv.vnflcmopocc \ + import views + +urlpatterns = [ + re_path(r'^$', views.IndexView.as_view(), name='index'), + re_path(r'^(?P[^/]+)/$', views.DetailView.as_view(), name='detail'), +] diff --git a/tacker_horizon/openstack_dashboard/dashboards/nfv/vnflcmopocc/views.py b/tacker_horizon/openstack_dashboard/dashboards/nfv/vnflcmopocc/views.py new file mode 100644 index 0000000..1609f46 --- /dev/null +++ b/tacker_horizon/openstack_dashboard/dashboards/nfv/vnflcmopocc/views.py @@ -0,0 +1,73 @@ +# Copyright (C) 2024 Fujitsu +# All Rights Reserved. +# +# 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.urls import reverse +from django.utils.translation import gettext_lazy as _ + +from horizon import exceptions +from horizon import tabs +from horizon.utils import memoized + +from tacker_horizon.openstack_dashboard import api as tacker_api +from tacker_horizon.openstack_dashboard.dashboards.nfv.vnflcmopocc \ + import tabs as vnflcmopocc_tabs + + +class IndexView(tabs.TabbedTableView): + # A very simple class-based view... + tab_group_class = vnflcmopocc_tabs.VNFLcmOpOccTabs + template_name = 'nfv/vnflcmopocc/index.html' + + def get_data(self, request, context, *args, **kwargs): + # Add data to the context here... + return context + + +class DetailView(tabs.TabView): + tab_group_class = vnflcmopocc_tabs.VNFOpOccDetailTabs + template_name = 'nfv/vnflcmopocc/detail.html' + redirect_url = 'horizon:nfv:vnflcmopocc:index' + page_title = _("LCM OP OCC Details: {{ id }}") + + def get_context_data(self, **kwargs): + context = super(DetailView, self).get_context_data(**kwargs) + vnflcmopocc = self.get_data() + context['vnflcmopocc'] = vnflcmopocc + context['id'] = kwargs['id'] + context['url'] = reverse(self.redirect_url) + return context + + @memoized.memoized_method + def get_data(self): + opocc_id = self.kwargs['id'] + + try: + vnflcmopocc = tacker_api.tacker.get_vnf_lcm_op_occ(self.request, + opocc_id) + vnflcmopocc['links'] = vnflcmopocc.get('_links', '') + return vnflcmopocc + except Exception: + redirect = reverse(self.redirect_url) + exceptions.handle( + self.request, + _('Failed to get VNF LCM operation occurrence. (id: %s)') % + opocc_id, + redirect=redirect) + raise exceptions.Http302(redirect) + + def get_tabs(self, request, *args, **kwargs): + vnflcmopocc = self.get_data() + return self.tab_group_class(request, vnflcmopocc=vnflcmopocc, **kwargs) diff --git a/tacker_horizon/openstack_dashboard/dashboards/nfv/vnfpackages/__init__.py b/tacker_horizon/openstack_dashboard/dashboards/nfv/vnfpackages/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tacker_horizon/openstack_dashboard/dashboards/nfv/vnfpackages/forms.py b/tacker_horizon/openstack_dashboard/dashboards/nfv/vnfpackages/forms.py new file mode 100644 index 0000000..ef0c220 --- /dev/null +++ b/tacker_horizon/openstack_dashboard/dashboards/nfv/vnfpackages/forms.py @@ -0,0 +1,189 @@ +# Copyright (C) 2024 Fujitsu +# All Rights Reserved. +# +# 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. + +import os + +from django.core.files.uploadedfile import InMemoryUploadedFile +from django.core.files.uploadedfile import SimpleUploadedFile +from django.core.files.uploadedfile import TemporaryUploadedFile +from django.forms import ValidationError +from django.utils.translation import gettext_lazy as _ + +from horizon import exceptions +from horizon import forms +from horizon import messages +from oslo_log import log as logging + +from tacker_horizon.openstack_dashboard import api + +LOG = logging.getLogger(__name__) + + +class UploadVnfpkg(forms.SelfHandlingForm): + user_data = forms.JSONField( + label=_("User Data"), required=False) + source_type = forms.ChoiceField( + label=_('VNF Package Source'), + choices=[('url', _('URL')), ('file', _('File'))], + widget=forms.Select( + attrs={'class': 'switchable', 'data-slug': 'source'})) + url = forms.URLField( + widget=forms.TextInput(attrs={ + 'class': 'switched', + 'data-switch-on': 'source', + 'data-source-url': _('URL')}), + required=False) + user_name = forms.CharField( + widget=forms.TextInput(attrs={ + 'class': 'switched', + 'data-switch-on': 'source', + 'data-source-url': _('User Name')}), + required=False) + password = forms.CharField( + widget=forms.PasswordInput(attrs={ + 'class': 'switched', + 'data-switch-on': 'source', + 'data-source-url': _('Password')}), + required=False) + vnf_package = forms.FileField( + widget=forms.FileInput(attrs={ + 'class': 'switched', + 'data-switch-on': 'source', + 'data-source-file': _('VNF Package')}), + required=False) + + def __init__(self, request, *args, **kwargs): + super(UploadVnfpkg, self).__init__(request, *args, **kwargs) + + def clean(self): + data = super(UploadVnfpkg, self).clean() + + return data + + def handle(self, request, data): + try: + body = {} + user_data = data.get('user_data', None) + if user_data: + body['userDefinedData'] = user_data + response = api.tacker.create_vnf_package(request, body) + except Exception: + exceptions.handle(request, + _('Failed to create VNF Package.')) + return False + + try: + params = {} + source_type = data.get('source_type', None) + if source_type == 'url': + params['url'] = data.get('url', None) + user_name = data.get('user_name', None) + password = data.get('password', None) + if user_name: + params['userName'] = user_name + params['password'] = password + api.tacker.upload_vnf_package( + request, response['id'], **params) + messages.success( + request, + _('Accepted to upload VNF Package. ' + '(id: %s)') % response['id']) + else: + file = request.FILES['vnf_package'] + if isinstance(file, TemporaryUploadedFile): + # Hack to fool Django, so we can keep file open. + file.file._closer.close_called = True + elif isinstance(file, InMemoryUploadedFile): + # Clone a new file for InMemoryUploadedFile. + # Because the old one will be closed by Django. + file = SimpleUploadedFile( + file.name, file.read(), file.content_type) + try: + api.tacker.upload_vnf_package( + request, response['id'], file, **params) + messages.success( + request, + _('Accepted to upload VNF Package. ' + '(id: %s)') % response['id']) + finally: + try: + filename = str(file.file.name) + except AttributeError: + pass + else: + try: + os.remove(filename) + except OSError as e: + LOG.warning( + 'Failed to remove temporary file ' + '%(file)s (%(e)s)', + {'file': filename, 'e': e}) + + except Exception: + exceptions.handle(request, + _('Failed to upload VNF Package. (id: %s)') % + response['id']) + return False + + return True + + +class UpdateVnfpkg(forms.SelfHandlingForm): + operationalState = forms.ChoiceField( + label=_('Operational State'), + required=False, + choices=[(None, _(' ')), + ('ENABLED', _('ENABLED')), + ('DISABLED', _('DISABLED'))], + widget=forms.Select( + attrs={'class': 'switchable', 'data-slug': 'source'})) + userData = forms.JSONField(label=_('User Data'), + required=False) + + def __init__(self, request, *args, **kwargs): + super(UpdateVnfpkg, self).__init__(request, *args, **kwargs) + + def clean(self): + data = super(UpdateVnfpkg, self).clean() + operationalState = data.get('operationalState', None) + userData = data.get('userData', None) + if operationalState is None and userData is None: + raise ValidationError( + _("At least one of the \"operationalState\" or \ + \"userDefinedData\" parameters shall be present.")) + return data + + def handle(self, request, data): + try: + vnfpkg_id = request.resolver_match.kwargs.get('id', None) + body = {} + operationalState = data.get('operationalState', None) + if operationalState: + body['operationalState'] = operationalState + + userDefinedData = data.get('userData', None) + if userDefinedData: + body['userDefinedData'] = userDefinedData + api.tacker.update_vnf_package(request, vnfpkg_id, body) + messages.success(request, + _('Update VNF Package. (id: %s)') % vnfpkg_id) + + except Exception: + exceptions.handle(request, + _('Failed to update VNF Package. (id: %s)') % + vnfpkg_id) + return False + + return True diff --git a/tacker_horizon/openstack_dashboard/dashboards/nfv/vnfpackages/panel.py b/tacker_horizon/openstack_dashboard/dashboards/nfv/vnfpackages/panel.py new file mode 100644 index 0000000..dd000cc --- /dev/null +++ b/tacker_horizon/openstack_dashboard/dashboards/nfv/vnfpackages/panel.py @@ -0,0 +1,29 @@ +# Copyright (C) 2024 Fujitsu +# All Rights Reserved. +# +# 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.utils.translation import gettext_lazy as _ + +import horizon + +from tacker_horizon.openstack_dashboard.dashboards.nfv import dashboard + + +class Vnfpackages(horizon.Panel): + name = _("VNF Packages") + slug = "vnfpackages" + + +dashboard.Nfv.register(Vnfpackages) diff --git a/tacker_horizon/openstack_dashboard/dashboards/nfv/vnfpackages/tables.py b/tacker_horizon/openstack_dashboard/dashboards/nfv/vnfpackages/tables.py new file mode 100644 index 0000000..4650596 --- /dev/null +++ b/tacker_horizon/openstack_dashboard/dashboards/nfv/vnfpackages/tables.py @@ -0,0 +1,119 @@ +# Copyright (C) 2024 Fujitsu +# All Rights Reserved. +# +# 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.utils.translation import gettext_lazy as _ +from django.utils.translation import ngettext_lazy + +from horizon import exceptions +from horizon import messages +from horizon import tables +from openstack_dashboard import policy + +from tacker_horizon.openstack_dashboard import api + + +class MyFilterAction(tables.FilterAction): + name = "myfilter" + + +class DeleteVNFPackage(policy.PolicyTargetMixin, tables.DeleteAction): + @staticmethod + def action_present(count): + return ngettext_lazy( + "Delete VNF Package", + "Delete VNF Packages", + count + ) + + @staticmethod + def action_past(count): + return ngettext_lazy( + "Delete VNF Package", + "Delete VNF Packages", + count + ) + + def action(self, request, obj_id): + api.tacker.delete_vnf_package(request, obj_id) + + +class UploadVNFPackage(tables.LinkAction): + name = "uploadvnfpkg" + verbose_name = _("Upload VNF Package") + classes = ("ajax-modal",) + icon = "plus" + url = "horizon:nfv:vnfpackages:uploadvnfpkg" + + +class FetchVNFPackage(tables.LinkAction): + name = "fetchvnfpkg" + verbose_name = _("Fetch VNF Package") + verbose_name_plural = _("Fetch VNF Package") + icon = "download" + url = "horizon:nfv:vnfpackages:fetch" + + def allowed(self, request, datum=None): + return True + + +class UpdateVNFPackageInfo(tables.LinkAction): + name = "updatevnfpkginfo" + verbose_name = _("Update VNF Package Info") + url = "horizon:nfv:vnfpackages:updatevnfpkg" + classes = ("ajax-modal",) + + +class CreateVNFIdentifier(tables.Action): + name = "createvnfid" + verbose_name = _("Create VNF Identifier") + + def single(self, table, request, obj_id): # pylint: disable=E0202 + try: + vnf_pkg = api.tacker.get_vnf_package(request, obj_id) + vnfd_id = vnf_pkg.get('vnfdId', None) + if not vnfd_id: + msg = _('Failed to get VNFD ID') + raise Exception(msg) + req_body = {} + req_body['vnfdId'] = vnfd_id + response = api.tacker.create_vnf_instance(request, req_body) + messages.success(request, + _('Create VNF Identifier. (id: %s)') % + response['id']) + except Exception: + exceptions.handle(request, + _('Failed to create VNF Identifier.')) + + +class VNFPackageTable(tables.DataTable): + id = tables.Column('id', verbose_name=_("ID"), + link="horizon:nfv:vnfpackages:detail",) + name = tables.Column('vnf_product_name', + verbose_name=_("VNF Product Name")) + onboarding_state = tables.Column('onboarding_state', + verbose_name=_("Onboarding State")) + usage_state = tables.Column('usage_state', verbose_name=_("Usage State")) + operational_state = tables.Column('operational_state', + verbose_name=_("Operational State")) + + class Meta(object): + name = "vnfpackage" + verbose_name = _("VNF Package") + pagination_param = 'package_marker' + prev_pagination_param = 'prev_package_marker' + table_actions = (UploadVNFPackage, DeleteVNFPackage, MyFilterAction,) + row_actions = (FetchVNFPackage, UpdateVNFPackageInfo, DeleteVNFPackage, + CreateVNFIdentifier,) diff --git a/tacker_horizon/openstack_dashboard/dashboards/nfv/vnfpackages/tabs.py b/tacker_horizon/openstack_dashboard/dashboards/nfv/vnfpackages/tabs.py new file mode 100644 index 0000000..11cd4bd --- /dev/null +++ b/tacker_horizon/openstack_dashboard/dashboards/nfv/vnfpackages/tabs.py @@ -0,0 +1,118 @@ +# Copyright (C) 2024 Fujitsu +# All Rights Reserved. +# +# 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.utils.translation import gettext_lazy as _ + +from horizon import exceptions +from horizon import tabs +from horizon import utils as horizon_utils + +from tacker_horizon.openstack_dashboard import api +from tacker_horizon.openstack_dashboard.dashboards.nfv import utils # noqa +from tacker_horizon.openstack_dashboard.dashboards.nfv.vnfpackages \ + import tables + + +class VNFPackageItem(object): + def __init__(self, vnfpkg_id, vnf_product_name, onboarding_state, + usage_state, operational_state, vnfd_id,): + self.id = vnfpkg_id + self.name = vnfpkg_id + self.vnf_product_name = vnf_product_name + self.onboarding_state = onboarding_state + self.usage_state = usage_state + self.operational_state = operational_state + self.vnfd_id = vnfd_id + + +class VNFPkgTab(tabs.TableTab): + name = _("VNFP tab") + slug = "vnfpkg_tab" + table_classes = (tables.VNFPackageTable,) + template_name = ("horizon/common/_detail_table.html") + preload = False + + def has_prev_data(self, table): + return self._has_prev + + def has_more_data(self, table): + return self._has_more + + def get_vnfpackage_data(self): + try: + self._has_prev = False + page_size = horizon_utils.functions.get_page_size(self.request) + marker = self.request.GET.get( + "package_marker", None) + prev_marker = self.request.GET.get( + "prev_package_marker", None) + packages = api.tacker.list_vnf_packages(self.request) + if marker is not None or prev_marker is not None: + for i, package in enumerate(packages): + if package["id"] == marker and i < len(packages) - 1: + packages = packages[i + 1:] + self._has_prev = True + break + if package["id"] == prev_marker and i > page_size: + packages = packages[i - page_size:] + self._has_prev = True + break + + if len(packages) > page_size: + self._has_more = True + else: + self._has_more = False + + rows = [] + for i, package in enumerate(packages): + if i >= page_size: + break + item = VNFPackageItem( + vnfpkg_id=package.get('id', ''), + vnf_product_name=package.get('vnfProductName', ''), + onboarding_state=package.get('onboardingState', ''), + usage_state=package.get('usageState', ''), + operational_state=package.get('operationalState', ''), + vnfd_id=package.get('vnfdId', ''),) + rows.append(item) + return rows + except Exception: + self._has_more = False + error_message = _('Failed to get VNF Packages.') + exceptions.handle(self.request, error_message) + + return [] + + +class VNFPkgTabs(tabs.TabGroup): + slug = "vnfpkg_tabs" + tabs = (VNFPkgTab,) + sticky = True + + +class VNFPkgDetailTab(tabs.Tab): + name = _("VNFPkg Detail") + slug = "vnfpkg_detail_tab" + template_name = "nfv/vnfpackages/vnfpkg_detail.html" + + def get_context_data(self, request): + return {'vnfpkg': self.tab_group.kwargs['vnfpkg']} + + +class VNFPkgDetailTabs(tabs.TabGroup): + slug = "vnfpkg_detail_tabs" + tabs = (VNFPkgDetailTab,) + sticky = True diff --git a/tacker_horizon/openstack_dashboard/dashboards/nfv/vnfpackages/templates/vnfpackages/_update_vnfpkg.html b/tacker_horizon/openstack_dashboard/dashboards/nfv/vnfpackages/templates/vnfpackages/_update_vnfpkg.html new file mode 100644 index 0000000..84b88b2 --- /dev/null +++ b/tacker_horizon/openstack_dashboard/dashboards/nfv/vnfpackages/templates/vnfpackages/_update_vnfpkg.html @@ -0,0 +1,9 @@ +{% extends "horizon/common/_modal_form.html" %} +{% load i18n %} + +{% block form_attrs %}enctype="multipart/form-data"{% endblock %} + +{% block modal-body-right %} +

{% trans "Description:" %}

+

{% trans "Update a VNF Package information." %}

+{% endblock %} diff --git a/tacker_horizon/openstack_dashboard/dashboards/nfv/vnfpackages/templates/vnfpackages/_upload_vnfpkg.html b/tacker_horizon/openstack_dashboard/dashboards/nfv/vnfpackages/templates/vnfpackages/_upload_vnfpkg.html new file mode 100644 index 0000000..b3b71b3 --- /dev/null +++ b/tacker_horizon/openstack_dashboard/dashboards/nfv/vnfpackages/templates/vnfpackages/_upload_vnfpkg.html @@ -0,0 +1,9 @@ +{% extends "horizon/common/_modal_form.html" %} +{% load i18n %} + +{% block form_attrs %}enctype="multipart/form-data"{% endblock %} + +{% block modal-body-right %} +

{% trans "Description:" %}

+

{% trans "Upload a VNF Package." %}

+{% endblock %} diff --git a/tacker_horizon/openstack_dashboard/dashboards/nfv/vnfpackages/templates/vnfpackages/detail.html b/tacker_horizon/openstack_dashboard/dashboards/nfv/vnfpackages/templates/vnfpackages/detail.html new file mode 100644 index 0000000..edb504b --- /dev/null +++ b/tacker_horizon/openstack_dashboard/dashboards/nfv/vnfpackages/templates/vnfpackages/detail.html @@ -0,0 +1,15 @@ +{% extends 'base.html' %} +{% load i18n %} +{% block title %}{% trans "VNF Package Details" %}{% endblock %} + +{% block page_header %} + {% include "horizon/common/_page_header.html" with title=page_title %} +{% endblock page_header %} + +{% block main %} +
+
+ {{ tab_group.render }} +
+
+{% endblock %} diff --git a/tacker_horizon/openstack_dashboard/dashboards/nfv/vnfpackages/templates/vnfpackages/index.html b/tacker_horizon/openstack_dashboard/dashboards/nfv/vnfpackages/templates/vnfpackages/index.html new file mode 100644 index 0000000..82131a5 --- /dev/null +++ b/tacker_horizon/openstack_dashboard/dashboards/nfv/vnfpackages/templates/vnfpackages/index.html @@ -0,0 +1,15 @@ +{% extends 'base.html' %} +{% load i18n %} +{% block title %}{% trans "VNF Packages" %}{% endblock %} + +{% block page_header %} + {% include "horizon/common/_page_header.html" with title=_("VNF Packages") %} +{% endblock page_header %} + +{% block main %} +
+
+ {{ tab_group.render }} +
+
+{% endblock %} diff --git a/tacker_horizon/openstack_dashboard/dashboards/nfv/vnfpackages/templates/vnfpackages/update_vnfpkg.html b/tacker_horizon/openstack_dashboard/dashboards/nfv/vnfpackages/templates/vnfpackages/update_vnfpkg.html new file mode 100644 index 0000000..ca3738d --- /dev/null +++ b/tacker_horizon/openstack_dashboard/dashboards/nfv/vnfpackages/templates/vnfpackages/update_vnfpkg.html @@ -0,0 +1,11 @@ +{% extends 'base.html' %} +{% load i18n %} +{% block title %}{% trans "Update VNF Package" %}{% endblock %} + +{% block page_header %} + {% include "horizon/common/_page_header.html" with title=_("Update VNF Package") %} +{% endblock page_header %} + +{% block main %} + {% include 'nfv/vnfpackages/_update_vnfpkg.html' %} +{% endblock %} diff --git a/tacker_horizon/openstack_dashboard/dashboards/nfv/vnfpackages/templates/vnfpackages/upload_vnfpkg.html b/tacker_horizon/openstack_dashboard/dashboards/nfv/vnfpackages/templates/vnfpackages/upload_vnfpkg.html new file mode 100644 index 0000000..4289e45 --- /dev/null +++ b/tacker_horizon/openstack_dashboard/dashboards/nfv/vnfpackages/templates/vnfpackages/upload_vnfpkg.html @@ -0,0 +1,11 @@ +{% extends 'base.html' %} +{% load i18n %} +{% block title %}{% trans "Upload VNF Package" %}{% endblock %} + +{% block page_header %} + {% include "horizon/common/_page_header.html" with title=_("Upload VNF Package") %} +{% endblock page_header %} + +{% block main %} + {% include 'nfv/vnfpackages/_upload_vnfpkg.html' %} +{% endblock %} diff --git a/tacker_horizon/openstack_dashboard/dashboards/nfv/vnfpackages/templates/vnfpackages/vnfpkg_detail.html b/tacker_horizon/openstack_dashboard/dashboards/nfv/vnfpackages/templates/vnfpackages/vnfpkg_detail.html new file mode 100644 index 0000000..ca3e894 --- /dev/null +++ b/tacker_horizon/openstack_dashboard/dashboards/nfv/vnfpackages/templates/vnfpackages/vnfpkg_detail.html @@ -0,0 +1,34 @@ +{% load i18n %} + +
+
+
{% trans "Additional Artifacts" %}
+
{{ vnfpkg.additionalArtifacts }}
+
{% trans "Checksum" %}
+
{{ vnfpkg.checksum }}
+
{% trans "ID" %}
+
{{ vnfpkg.id }}
+
{% trans "Links" %}
+
{{ vnfpkg.links }}
+
{% trans "Onboarding State" %}
+
{{ vnfpkg.onboardingState }}
+
{% trans "Operational State" %}
+
{{ vnfpkg.operationalState }}
+
{% trans "Software Images" %}
+
{{ vnfpkg.softwareImages }}
+
{% trans "Usage State" %}
+
{{ vnfpkg.usageState }}
+
{% trans "User Defined Data" %}
+
{{ vnfpkg.userDefinedData }}
+
{% trans "VNF Product Name" %}
+
{{ vnfpkg.vnfProductName }}
+
{% trans "VNF Provider" %}
+
{{ vnfpkg.vnfProvider }}
+
{% trans "VNF Software Version" %}
+
{{ vnfpkg.vnfSoftwareVersion }}
+
{% trans "VNFD ID" %}
+
{{ vnfpkg.vnfdId }}
+
{% trans "VNFD Version" %}
+
{{ vnfpkg.vnfdVersion }}
+
+
diff --git a/tacker_horizon/openstack_dashboard/dashboards/nfv/vnfpackages/urls.py b/tacker_horizon/openstack_dashboard/dashboards/nfv/vnfpackages/urls.py new file mode 100644 index 0000000..025036d --- /dev/null +++ b/tacker_horizon/openstack_dashboard/dashboards/nfv/vnfpackages/urls.py @@ -0,0 +1,31 @@ +# Copyright (C) 2024 Fujitsu +# All Rights Reserved. +# +# 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.urls import re_path + +from tacker_horizon.openstack_dashboard.dashboards.nfv.vnfpackages \ + import views + +urlpatterns = [ + re_path(r'^$', views.IndexView.as_view(), name='index'), + re_path(r'^(?P[^/]+)/fetch/$', views.fetch_vnf_package, + name='fetch'), + re_path(r'^uploadvnfpkg$', views.UploadVNFPkgView.as_view(), + name='uploadvnfpkg'), + re_path(r'^(?P[^/]+)/$', views.DetailView.as_view(), name='detail'), + re_path(r'^(?P[^/]+)/updatevnfpkg/$', + views.UpdateVNFPkgView.as_view(), name='updatevnfpkg'), +] diff --git a/tacker_horizon/openstack_dashboard/dashboards/nfv/vnfpackages/views.py b/tacker_horizon/openstack_dashboard/dashboards/nfv/vnfpackages/views.py new file mode 100644 index 0000000..9adf9b4 --- /dev/null +++ b/tacker_horizon/openstack_dashboard/dashboards/nfv/vnfpackages/views.py @@ -0,0 +1,121 @@ +# Copyright (C) 2024 Fujitsu +# All Rights Reserved. +# +# 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 import http +from django.urls import reverse +from django.urls import reverse_lazy +from django.utils.translation import gettext_lazy as _ + +from horizon import exceptions +from horizon import forms +from horizon import tabs +from horizon.utils import memoized + +from tacker_horizon.openstack_dashboard import api as tacker_api +from tacker_horizon.openstack_dashboard.dashboards.nfv.vnfpackages \ + import forms as project_forms +from tacker_horizon.openstack_dashboard.dashboards.nfv.vnfpackages \ + import tabs as vnfpkg_tabs + + +def fetch_vnf_package(request, id): + try: + file = tacker_api.tacker.fetch_vnf_package(request, id) + + except Exception: + exceptions.handle(request, + _('Failed to fetch VNF Package. (id: %s)') % id, + redirect="horizon:nfv:vnfpackages:index") + response = http.HttpResponse(content_type='application/zip') + response.write(file) + response['Content-Disposition'] = ( + 'attachment; filename="package-%s.zip"' % id) + response['Content-Length'] = len(file) + + return response + + +class IndexView(tabs.TabbedTableView): + tab_group_class = vnfpkg_tabs.VNFPkgTabs + template_name = 'nfv/vnfpackages/index.html' + + +class UploadVNFPkgView(forms.ModalFormView): + form_class = project_forms.UploadVnfpkg + template_name = 'nfv/vnfpackages/upload_vnfpkg.html' + success_url = reverse_lazy("horizon:nfv:vnfpackages:index") + modal_id = "add_service_modal" + modal_header = _("Upload VNF Package") + submit_label = _("Upload VNF Package") + submit_url = "horizon:nfv:vnfpackages:uploadvnfpkg" + + def get_context_data(self, **kwargs): + context = super(UploadVNFPkgView, self).get_context_data(**kwargs) + context['submit_url'] = reverse(self.submit_url) + return context + + +class UpdateVNFPkgView(forms.ModalFormView): + form_class = project_forms.UpdateVnfpkg + template_name = 'nfv/vnfpackages/update_vnfpkg.html' + success_url = reverse_lazy("horizon:nfv:vnfpackages:index") + modal_id = "add_service_modal" + modal_header = _("Update VNF Package Info") + submit_label = _("Update VNF Package Info") + submit_url = "horizon:nfv:vnfpackages:updatevnfpkg" + + def get_context_data(self, **kwargs): + context = super(UpdateVNFPkgView, self).get_context_data(**kwargs) + context['id'] = self.kwargs['id'] + context['submit_url'] = reverse(self.submit_url, + kwargs={'id': self.kwargs['id']}) + return context + + +class DetailView(tabs.TabView): + tab_group_class = vnfpkg_tabs.VNFPkgDetailTabs + template_name = 'nfv/vnfpackages/detail.html' + redirect_url = 'horizon:nfv:vnfpackages:index' + page_title = _("VNF Package Details: {{ id }}") + + def get_context_data(self, **kwargs): + context = super(DetailView, self).get_context_data(**kwargs) + vnfpkg = self.get_data() + context['vnfpkg'] = vnfpkg + context['id'] = kwargs['id'] + context['url'] = reverse(self.redirect_url) + + return context + + @memoized.memoized_method + def get_data(self): + vnfpkg_id = self.kwargs.get('id', None) + + try: + vnfpkg = tacker_api.tacker.get_vnf_package(self.request, vnfpkg_id) + vnfpkg['links'] = vnfpkg.get('_links', '') + return vnfpkg + except Exception: + redirect = reverse(self.redirect_url) + exceptions.handle( + self.request, + _('Failed to get VNF Package. (id: %s)') % vnfpkg_id, + redirect=redirect) + raise exceptions.Http302(redirect) + + def get_tabs(self, request, *args, **kwargs): + vnfpkg = self.get_data() + return self.tab_group_class(request, vnfpkg=vnfpkg, **kwargs) diff --git a/tacker_horizon/openstack_dashboard/dashboards/nfv/vnfpmjob/__init__.py b/tacker_horizon/openstack_dashboard/dashboards/nfv/vnfpmjob/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tacker_horizon/openstack_dashboard/dashboards/nfv/vnfpmjob/forms.py b/tacker_horizon/openstack_dashboard/dashboards/nfv/vnfpmjob/forms.py new file mode 100644 index 0000000..0d4dc1d --- /dev/null +++ b/tacker_horizon/openstack_dashboard/dashboards/nfv/vnfpmjob/forms.py @@ -0,0 +1,102 @@ +# Copyright (C) 2024 Fujitsu +# All Rights Reserved. +# +# 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. + +import json + +from django.utils.translation import gettext_lazy as _ + +from horizon import exceptions +from horizon import forms +from horizon import messages + +from tacker_horizon.openstack_dashboard import api + + +class CreatePMJob(forms.SelfHandlingForm): + param_file = forms.FileField( + label=_("Param File"), + help_text=_("parameter file to upload."), + widget=forms.FileInput( + attrs={'class': 'switched', + 'data-switch-on': 'source', + 'data-source-file': _('Param File')})) + + def __init__(self, request, *args, **kwargs): + super(CreatePMJob, self).__init__(request, *args, **kwargs) + + def clean(self): + data = super(CreatePMJob, self).clean() + + try: + param_str = self.files['param_file'].read() + param = json.loads(param_str) + data['param'] = param + except Exception as e: + msg = _('Failed to read file: %s.') % e + raise forms.ValidationError(msg) + + return data + + def handle(self, request, data): + try: + param = data['param'] + pmjob = api.tacker.create_pm_job(request, param) + messages.success(request, + _('Create PM Job. (id: %s)') % + pmjob['id']) + return True + except Exception: + msg = _('Failed to create PM Job.') + exceptions.handle(request, msg) + return False + + +class UpdatePMJob(forms.SelfHandlingForm): + param_file = forms.FileField( + label=_("Param File"), + help_text=_("parameter file to upload."), + widget=forms.FileInput( + attrs={'class': 'switched', + 'data-switch-on': 'source', + 'data-source-file': _('Param File')})) + + def __init__(self, request, *args, **kwargs): + super(UpdatePMJob, self).__init__(request, *args, **kwargs) + + def clean(self): + data = super(UpdatePMJob, self).clean() + + try: + param_str = self.files['param_file'].read() + param = json.loads(param_str) + data['param'] = param + except Exception as e: + msg = _('Failed to read file: %s.') % e + raise forms.ValidationError(msg) + + return data + + def handle(self, request, data): + try: + param = data['param'] + job_id = request.resolver_match.kwargs['id'] + api.tacker.update_pm_job(request, job_id, param) + messages.success(request, + _('Update PM Job. (id: %s)') % job_id) + return True + except Exception: + msg = _('Failed to update PM Job. (id: %s)') % job_id + exceptions.handle(request, msg) + return False diff --git a/tacker_horizon/openstack_dashboard/dashboards/nfv/vnfpmjob/panel.py b/tacker_horizon/openstack_dashboard/dashboards/nfv/vnfpmjob/panel.py new file mode 100644 index 0000000..e5f5494 --- /dev/null +++ b/tacker_horizon/openstack_dashboard/dashboards/nfv/vnfpmjob/panel.py @@ -0,0 +1,29 @@ +# Copyright (C) 2024 Fujitsu +# All Rights Reserved. +# +# 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.utils.translation import gettext_lazy as _ + +import horizon + +from tacker_horizon.openstack_dashboard.dashboards.nfv import dashboard + + +class VnfPMJob(horizon.Panel): + name = _("PM Job") + slug = "vnfpmjob" + + +dashboard.Nfv.register(VnfPMJob) diff --git a/tacker_horizon/openstack_dashboard/dashboards/nfv/vnfpmjob/tables.py b/tacker_horizon/openstack_dashboard/dashboards/nfv/vnfpmjob/tables.py new file mode 100644 index 0000000..83964e9 --- /dev/null +++ b/tacker_horizon/openstack_dashboard/dashboards/nfv/vnfpmjob/tables.py @@ -0,0 +1,78 @@ +# Copyright (C) 2024 Fujitsu +# All Rights Reserved. +# +# 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.utils.translation import gettext_lazy as _ +from django.utils.translation import ngettext_lazy + +from horizon import tables +from openstack_dashboard import policy + +from tacker_horizon.openstack_dashboard import api + + +class MyFilterAction(tables.FilterAction): + name = "myfilter" + + +class DeletePMJob(policy.PolicyTargetMixin, tables.DeleteAction): + @staticmethod + def action_present(count): + return ngettext_lazy( + "Delete PM Job", + "Delete PM Jobs", + count + ) + + @staticmethod + def action_past(count): + return ngettext_lazy( + "Delete PM Job", + "Delete PM Jobs", + count + ) + + def action(self, request, obj_id): + api.tacker.delete_pm_job(request, obj_id) + + +class CreatePMJob(tables.LinkAction): + name = "createpmjob" + verbose_name = _("Create PM Job") + classes = ("ajax-modal",) + icon = "plus" + url = "horizon:nfv:vnfpmjob:createpmjob" + + +class UpdatePMJob(tables.LinkAction): + name = "updatepmjob" + verbose_name = _("Update PM Job") + url = "horizon:nfv:vnfpmjob:updatepmjob" + classes = ("ajax-modal",) + + +class VNFPMJobTable(tables.DataTable): + id = tables.Column('id', link="horizon:nfv:vnfpmjob:detail", + verbose_name=_("ID")) + object_type = tables.Column('object_type', verbose_name=_("Object Type")) + links = tables.Column('links', verbose_name=_("Links")) + + class Meta(object): + name = "pmjob" + verbose_name = _("PM Job") + pagination_param = 'vnfpmjob_marker' + prev_pagination_param = 'prev_vnfpmjob_marker' + table_actions = (CreatePMJob, DeletePMJob, MyFilterAction,) + row_actions = (UpdatePMJob, DeletePMJob,) diff --git a/tacker_horizon/openstack_dashboard/dashboards/nfv/vnfpmjob/tabs.py b/tacker_horizon/openstack_dashboard/dashboards/nfv/vnfpmjob/tabs.py new file mode 100644 index 0000000..b1e92e4 --- /dev/null +++ b/tacker_horizon/openstack_dashboard/dashboards/nfv/vnfpmjob/tabs.py @@ -0,0 +1,122 @@ +# Copyright (C) 2024 Fujitsu +# All Rights Reserved. +# +# 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.utils.translation import gettext_lazy as _ + +from horizon import exceptions +from horizon import tabs +from horizon import utils as horizon_utils + +from tacker_horizon.openstack_dashboard import api +from tacker_horizon.openstack_dashboard.dashboards.nfv.vnfpmjob \ + import tables + + +class VNFPMJobItem(object): + def __init__(self, job_id, links, object_type): + self.id = job_id + self.name = job_id + self.links = links + self.object_type = object_type + + +class VNFPMJobTab(tabs.TableTab): + name = _("VNFPMJob Tab") + slug = "vnfpmjob_tab" + table_classes = (tables.VNFPMJobTable,) + template_name = "horizon/common/_detail_table.html" + preload = False + + def has_prev_data(self, table): + return self._has_prev + + def has_more_data(self, table): + return self._has_more + + def get_pmjob_data(self): + try: + self._has_prev = False + page_size = horizon_utils.functions.get_page_size(self.request) + marker = self.request.GET.get("vnfpmjob_marker", None) + prev_marker = self.request.GET.get("prev_vnfpmjob_marker", None) + pmjobs = api.tacker.list_pm_jobs(self.request) + + if marker is not None or prev_marker is not None: + for i, pmjob in enumerate(pmjobs): + if pmjob["id"] == marker and i < len(pmjobs) - 1: + pmjobs = pmjobs[i + 1:] + self._has_prev = True + break + if pmjob["id"] == prev_marker and i > page_size: + pmjobs = pmjobs[i - page_size:] + self._has_prev = True + break + + if len(pmjobs) > page_size: + self._has_more = True + else: + self._has_more = False + + rows = [] + for i, pmjob in enumerate(pmjobs): + if i >= page_size: + break + item = VNFPMJobItem(pmjob['id'], + pmjob['_links'], + pmjob['objectType']) + rows.append(item) + return rows + except Exception: + self._has_more = False + error_message = _('Failed to get PM Jobs.') + exceptions.handle(self.request, error_message) + return [] + + +class VNFPMJobTabs(tabs.TabGroup): + slug = "vnfpmjob_tabs" + tabs = (VNFPMJobTab,) + sticky = True + + +class VNFPMJobDetailTab(tabs.Tab): + name = _("VNF PM Job") + slug = "vnfpmjob_detail_tab" + template_name = "nfv/vnfpmjob/pmjob_detail.html" + + def get_context_data(self, request): + return {'pmjob': self.tab_group.kwargs['pmjob']} + + +class VNFPMJobDetailTabs(tabs.TabGroup): + slug = "vnfpmjob_detail_tabs" + tabs = (VNFPMJobDetailTab,) + sticky = True + + +class VNFPMJobReportDetailTab(tabs.Tab): + name = _("PM Job Reports") + slug = "vnfpmjob_report_detail_tab" + template_name = "nfv/vnfpmjob/report_detail.html" + + def get_context_data(self, request): + return {'report': self.tab_group.kwargs['report']} + + +class VNFPMJobReportDetailTabs(tabs.TabGroup): + slug = "vnfpmjob_report_detail_tabs" + tabs = (VNFPMJobReportDetailTab,) + sticky = True diff --git a/tacker_horizon/openstack_dashboard/dashboards/nfv/vnfpmjob/templates/vnfpmjob/_create_pmjob.html b/tacker_horizon/openstack_dashboard/dashboards/nfv/vnfpmjob/templates/vnfpmjob/_create_pmjob.html new file mode 100644 index 0000000..24d6776 --- /dev/null +++ b/tacker_horizon/openstack_dashboard/dashboards/nfv/vnfpmjob/templates/vnfpmjob/_create_pmjob.html @@ -0,0 +1,9 @@ +{% extends "horizon/common/_modal_form.html" %} +{% load i18n %} + +{% block form_attrs %}enctype="multipart/form-data"{% endblock %} + +{% block modal-body-right %} +

{% trans "Description:" %}

+

{% trans "Create a PM Job." %}

+{% endblock %} diff --git a/tacker_horizon/openstack_dashboard/dashboards/nfv/vnfpmjob/templates/vnfpmjob/_update_pmjob.html b/tacker_horizon/openstack_dashboard/dashboards/nfv/vnfpmjob/templates/vnfpmjob/_update_pmjob.html new file mode 100644 index 0000000..be5a05b --- /dev/null +++ b/tacker_horizon/openstack_dashboard/dashboards/nfv/vnfpmjob/templates/vnfpmjob/_update_pmjob.html @@ -0,0 +1,9 @@ +{% extends "horizon/common/_modal_form.html" %} +{% load i18n %} + +{% block form_attrs %}enctype="multipart/form-data"{% endblock %} + +{% block modal-body-right %} +

{% trans "Description:" %}

+

{% trans "Update a PM Job." %}

+{% endblock %} diff --git a/tacker_horizon/openstack_dashboard/dashboards/nfv/vnfpmjob/templates/vnfpmjob/create_pmjob.html b/tacker_horizon/openstack_dashboard/dashboards/nfv/vnfpmjob/templates/vnfpmjob/create_pmjob.html new file mode 100644 index 0000000..c365a8e --- /dev/null +++ b/tacker_horizon/openstack_dashboard/dashboards/nfv/vnfpmjob/templates/vnfpmjob/create_pmjob.html @@ -0,0 +1,11 @@ +{% extends 'base.html' %} +{% load i18n %} +{% block title %}{% trans "Create PM Job" %}{% endblock %} + +{% block page_header %} + {% include "horizon/common/_page_header.html" with title=_("Create PM Job") %} +{% endblock page_header %} + +{% block main %} + {% include 'nfv/vnfpmjob/_create_pmjob.html' %} +{% endblock %} diff --git a/tacker_horizon/openstack_dashboard/dashboards/nfv/vnfpmjob/templates/vnfpmjob/detail.html b/tacker_horizon/openstack_dashboard/dashboards/nfv/vnfpmjob/templates/vnfpmjob/detail.html new file mode 100644 index 0000000..2405701 --- /dev/null +++ b/tacker_horizon/openstack_dashboard/dashboards/nfv/vnfpmjob/templates/vnfpmjob/detail.html @@ -0,0 +1,15 @@ +{% extends 'base.html' %} +{% load i18n %} +{% block title %}{% trans "PM Job Detail" %}{% endblock %} + +{% block page_header %} + {% include "horizon/common/_page_header.html" with title=page_title %} +{% endblock page_header %} + +{% block main %} +
+
+ {{ tab_group.render }} +
+
+{% endblock %} diff --git a/tacker_horizon/openstack_dashboard/dashboards/nfv/vnfpmjob/templates/vnfpmjob/index.html b/tacker_horizon/openstack_dashboard/dashboards/nfv/vnfpmjob/templates/vnfpmjob/index.html new file mode 100644 index 0000000..a3f56c2 --- /dev/null +++ b/tacker_horizon/openstack_dashboard/dashboards/nfv/vnfpmjob/templates/vnfpmjob/index.html @@ -0,0 +1,15 @@ +{% extends 'base.html' %} +{% load i18n %} +{% block title %}{% trans "PM Job" %}{% endblock %} + +{% block page_header %} + {% include "horizon/common/_page_header.html" with title=_("PM Job") %} +{% endblock page_header %} + +{% block main %} +
+
+ {{ tab_group.render }} +
+
+{% endblock %} diff --git a/tacker_horizon/openstack_dashboard/dashboards/nfv/vnfpmjob/templates/vnfpmjob/pmjob_detail.html b/tacker_horizon/openstack_dashboard/dashboards/nfv/vnfpmjob/templates/vnfpmjob/pmjob_detail.html new file mode 100644 index 0000000..7a6d194 --- /dev/null +++ b/tacker_horizon/openstack_dashboard/dashboards/nfv/vnfpmjob/templates/vnfpmjob/pmjob_detail.html @@ -0,0 +1,57 @@ +{% load i18n %} + + + +
+
+
{% trans "ID" %}
+
{{ pmjob.id }}
+
{% trans "Links" %}
+
{{ pmjob.links }}
+
{% trans "Callback Uri" %}
+
{{ pmjob.callback_uri }}
+
{% trans "Criteria" %}
+
{{ pmjob.criteria }}
+
{% trans "Object Instance IDs" %}
+
{{ pmjob.object_instance_ids }}
+
{% trans "Object Type" %}
+
{{ pmjob.object_type }}
+
{% trans "Sub Object Instance IDs" %}
+
{{ pmjob.sub_object_instance_ids }}
+
{% trans "Reports" %}
+
+ + + + + + {% for report in pmjob.reports %} + + + + + {% endfor %} +
{% trans "readyTime" %}
{% trans "href" %}
{{ report.readyTime }} + {% if report.href %} + {% if report.id and report.reportId %} + {{ report.href }} + {% else %} + {% trans report.href %} + {% endif %} + {% else %} + {% trans "-" %} + {% endif %} +
+
+
+
diff --git a/tacker_horizon/openstack_dashboard/dashboards/nfv/vnfpmjob/templates/vnfpmjob/report_detail.html b/tacker_horizon/openstack_dashboard/dashboards/nfv/vnfpmjob/templates/vnfpmjob/report_detail.html new file mode 100644 index 0000000..57acb6e --- /dev/null +++ b/tacker_horizon/openstack_dashboard/dashboards/nfv/vnfpmjob/templates/vnfpmjob/report_detail.html @@ -0,0 +1,39 @@ +{% load i18n %} + + + +
+
+
{% trans "Entries" %}
+
+ + + + + + + + + {% for entrie in report.entries %} + + + + + + + + {% endfor %} +
{% trans "objectType" %}
{% trans "objectInstanceId" %}
{% trans "subObjectInstanceId" %}
{% trans "performanceMetric" %}
{% trans "performanceValues" %}
{{ entrie.objectType }}{{ entrie.objectInstanceId }}{{ entrie.subObjectInstanceId }}{{ entrie.performanceMetric }}{{ entrie.performanceValues }}
+
+
+
diff --git a/tacker_horizon/openstack_dashboard/dashboards/nfv/vnfpmjob/templates/vnfpmjob/update_pmjob.html b/tacker_horizon/openstack_dashboard/dashboards/nfv/vnfpmjob/templates/vnfpmjob/update_pmjob.html new file mode 100644 index 0000000..965b32c --- /dev/null +++ b/tacker_horizon/openstack_dashboard/dashboards/nfv/vnfpmjob/templates/vnfpmjob/update_pmjob.html @@ -0,0 +1,11 @@ +{% extends 'base.html' %} +{% load i18n %} +{% block title %}{% trans "Update PM Job" %}{% endblock %} + +{% block page_header %} + {% include "horizon/common/_page_header.html" with title=_("Update PM Job") %} +{% endblock page_header %} + +{% block main %} + {% include 'nfv/vnfpmjob/_update_pmjob.html' %} +{% endblock %} diff --git a/tacker_horizon/openstack_dashboard/dashboards/nfv/vnfpmjob/urls.py b/tacker_horizon/openstack_dashboard/dashboards/nfv/vnfpmjob/urls.py new file mode 100644 index 0000000..581889d --- /dev/null +++ b/tacker_horizon/openstack_dashboard/dashboards/nfv/vnfpmjob/urls.py @@ -0,0 +1,33 @@ +# Copyright (C) 2024 Fujitsu +# All Rights Reserved. +# +# 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.urls import re_path + +from tacker_horizon.openstack_dashboard.dashboards.nfv.vnfpmjob \ + import views + +urlpatterns = [ + re_path(r'^$', views.IndexView.as_view(), name='index'), + re_path(r'^createpmjob$', views.CreatePMJobView.as_view(), + name='createpmjob'), + re_path(r'^(?P[^/]+)/updatepmjob$', views.UpdatePMJobView.as_view(), + name='updatepmjob'), + re_path(r'^(?P[^/]+)/$', views.DetailView.as_view(), + name='detail'), + re_path(r'^(?P[^/]+)/report/(?P[^/]+)/detail$', + views.ReportDetailView.as_view(), + name='reportdetail'), +] diff --git a/tacker_horizon/openstack_dashboard/dashboards/nfv/vnfpmjob/views.py b/tacker_horizon/openstack_dashboard/dashboards/nfv/vnfpmjob/views.py new file mode 100644 index 0000000..9feb848 --- /dev/null +++ b/tacker_horizon/openstack_dashboard/dashboards/nfv/vnfpmjob/views.py @@ -0,0 +1,172 @@ +# Copyright (C) 2024 Fujitsu +# All Rights Reserved. +# +# 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.urls import reverse +from django.urls import reverse_lazy +from django.utils.translation import gettext_lazy as _ + +from horizon import exceptions +from horizon import forms +from horizon import tabs +from horizon.utils import memoized + +from tacker_horizon.openstack_dashboard import api as tacker_api +from tacker_horizon.openstack_dashboard.dashboards.nfv.vnfpmjob \ + import forms as project_forms +from tacker_horizon.openstack_dashboard.dashboards.nfv.vnfpmjob \ + import tabs as pmjob_tabs + + +class IndexView(tabs.TabbedTableView): + # A very simple class-based view... + tab_group_class = pmjob_tabs.VNFPMJobTabs + template_name = 'nfv/vnfpmjob/index.html' + + def get_data(self, request, context, *args, **kwargs): + # Add data to the context here... + return context + + +class CreatePMJobView(forms.ModalFormView): + form_class = project_forms.CreatePMJob + template_name = 'nfv/vnfpmjob/create_pmjob.html' + success_url = reverse_lazy("horizon:nfv:vnfpmjob:index") + modal_id = "create_pmjob_modal" + modal_header = _("Create PM Job") + submit_label = _("Create PM Job") + submit_url = "horizon:nfv:vnfpmjob:createpmjob" + + def get_context_data(self, **kwargs): + context = super(CreatePMJobView, self).get_context_data(**kwargs) + context['submit_url'] = reverse(self.submit_url) + return context + + +class UpdatePMJobView(forms.ModalFormView): + form_class = project_forms.UpdatePMJob + template_name = 'nfv/vnfpmjob/update_pmjob.html' + success_url = reverse_lazy("horizon:nfv:vnfpmjob:index") + modal_id = "update_pmjob_modal" + modal_header = _("Update PM Job") + submit_label = _("Update PM Job") + submit_url = "horizon:nfv:vnfpmjob:updatepmjob" + + def get_context_data(self, **kwargs): + context = super(UpdatePMJobView, self).get_context_data(**kwargs) + context['submit_url'] = reverse(self.submit_url, + kwargs={'id': self.kwargs['id']}) + return context + + +class DetailView(tabs.TabView): + tab_group_class = pmjob_tabs.VNFPMJobDetailTabs + template_name = 'nfv/vnfpmjob/detail.html' + redirect_url = 'horizon:nfv:vnfpmjob:index' + page_title = _("PM Job Detail: {{ id }}") + + def get_context_data(self, **kwargs): + context = super(DetailView, self).get_context_data(**kwargs) + pmjob = self.get_data() + context['pmjob'] = pmjob + context['id'] = kwargs['id'] + context['url'] = reverse(self.redirect_url) + return context + + @memoized.memoized_method + def get_data(self): + job_id = self.kwargs['id'] + + try: + pmjob = tacker_api.tacker.get_pm_job(self.request, job_id) + pmjob['links'] = pmjob.get('_links', '') + pmjob['callback_uri'] = pmjob.get('callbackUri', '') + pmjob['criteria'] = pmjob.get('criteria', '') + pmjob['id'] = pmjob.get('id', '') + pmjob['object_instance_ids'] = pmjob.get('objectInstanceIds', '') + pmjob['object_type'] = pmjob.get('objectType', '') + pmjob['sub_object_instance_ids'] = pmjob.get( + 'subObjectInstanceIds', '') + pmjob['reports'] = self.add_reportid(pmjob.get('reports', '')) + + return pmjob + except Exception: + redirect = reverse(self.redirect_url) + exceptions.handle(self.request, + _('Failed to get PM Job. (id: %s)') % + job_id, redirect=redirect) + raise exceptions.Http302(redirect) + + def get_tabs(self, request, *args, **kwargs): + pmjob = self.get_data() + return self.tab_group_class(request, pmjob=pmjob, **kwargs) + + def add_reportid(self, reports=None): + repots_new = [] + for report in reports: + href = report.get('href', '') + target_reports = 'reports/' + target_jobs = 'pm_jobs/' + idx_reports = href.find(target_reports) + idx_jobs = href.find(target_jobs) + + if (0 <= idx_reports) and (0 <= idx_jobs): + report['reportId'] = href[idx_reports+len(target_reports):] + report['id'] = href[idx_jobs+len(target_jobs):idx_reports-1] + + repots_new.extend([report]) + return repots_new + + +class ReportDetailView(tabs.TabView): + tab_group_class = pmjob_tabs.VNFPMJobReportDetailTabs + template_name = 'nfv/vnfpmjob/detail.html' + redirect_url = 'horizon:nfv:vnfpmjob:index' + page_title = _("PM Job Report: {{ reportId }}") + href_url = 'horizon:nfv:vnfpmjob:reportdetail' + + def get_context_data(self, **kwargs): + context = super(ReportDetailView, self).get_context_data(**kwargs) + report = self.get_data() + context['report'] = report + context['id'] = kwargs['id'] + context['reportId'] = kwargs['reportId'] + context['url'] = reverse(self.redirect_url) + context['submit_url'] = reverse( + self.href_url, kwargs={'id': self.kwargs['id'], + 'reportId': self.kwargs['reportId']}) + return context + + @memoized.memoized_method + def get_data(self): + + job_id = self.kwargs['id'] + report_id = self.kwargs['reportId'] + + try: + report = tacker_api.tacker.get_pm_report(self.request, + job_id, report_id) + report['entries'] = report.get('entries', '') + return report + except Exception: + redirect = reverse(self.redirect_url) + exceptions.handle(self.request, + _('Failed to get Report. (id: %s)') % report_id, + redirect=redirect) + raise exceptions.Http302(redirect) + + def get_tabs(self, request, *args, **kwargs): + report = self.get_data() + return self.tab_group_class(request, report=report, **kwargs) diff --git a/tacker_horizon/openstack_dashboard/dashboards/nfv/vnfpmthreshold/__init__.py b/tacker_horizon/openstack_dashboard/dashboards/nfv/vnfpmthreshold/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tacker_horizon/openstack_dashboard/dashboards/nfv/vnfpmthreshold/forms.py b/tacker_horizon/openstack_dashboard/dashboards/nfv/vnfpmthreshold/forms.py new file mode 100644 index 0000000..5796244 --- /dev/null +++ b/tacker_horizon/openstack_dashboard/dashboards/nfv/vnfpmthreshold/forms.py @@ -0,0 +1,102 @@ +# Copyright (C) 2024 Fujitsu +# All Rights Reserved. +# +# 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. + +import json + +from django.utils.translation import gettext_lazy as _ + +from horizon import exceptions +from horizon import forms +from horizon import messages + +from tacker_horizon.openstack_dashboard import api + + +class CreatePMThreshold(forms.SelfHandlingForm): + param_file = forms.FileField( + label=_("Param File"), + help_text=_("parameter file to upload."), + widget=forms.FileInput( + attrs={'class': 'switched', + 'data-switch-on': 'source', + 'data-source-file': _('Param File')})) + + def __init__(self, request, *args, **kwargs): + super(CreatePMThreshold, self).__init__(request, *args, **kwargs) + + def clean(self): + data = super(CreatePMThreshold, self).clean() + + try: + param_str = self.files['param_file'].read() + param = json.loads(param_str) + data['param'] = param + except Exception as e: + msg = _('Failed to read file: %s.') % e + raise forms.ValidationError(msg) + + return data + + def handle(self, request, data): + try: + param = data['param'] + pmthreshold = api.tacker.create_pm_threshold(request, param) + messages.success(request, + _('Create PM Threshold. (id: %s)') % + pmthreshold['id']) + return True + except Exception: + msg = _('Failed to create PM Threshold.') + exceptions.handle(request, msg) + return False + + +class UpdatePMThreshold(forms.SelfHandlingForm): + param_file = forms.FileField( + label=_("Param File"), + help_text=_("parameter file to upload."), + widget=forms.FileInput( + attrs={'class': 'switched', + 'data-switch-on': 'source', + 'data-source-file': _('Param File')})) + + def __init__(self, request, *args, **kwargs): + super(UpdatePMThreshold, self).__init__(request, *args, **kwargs) + + def clean(self): + data = super(UpdatePMThreshold, self).clean() + + try: + param_str = self.files['param_file'].read() + param = json.loads(param_str) + data['param'] = param + except Exception as e: + msg = _('Failed to read file: %s.') % e + raise forms.ValidationError(msg) + + return data + + def handle(self, request, data): + try: + param = data['param'] + threshold_id = request.resolver_match.kwargs['id'] + api.tacker.update_pm_threshold(request, threshold_id, param) + messages.success(request, + _('Update PM Threshold. (id: %s)') % threshold_id) + return True + except Exception: + msg = _('Failed to update PM Threshold. (id: %s)') % threshold_id + exceptions.handle(request, msg) + return False diff --git a/tacker_horizon/openstack_dashboard/dashboards/nfv/vnfpmthreshold/panel.py b/tacker_horizon/openstack_dashboard/dashboards/nfv/vnfpmthreshold/panel.py new file mode 100644 index 0000000..25083c4 --- /dev/null +++ b/tacker_horizon/openstack_dashboard/dashboards/nfv/vnfpmthreshold/panel.py @@ -0,0 +1,29 @@ +# Copyright (C) 2024 Fujitsu +# All Rights Reserved. +# +# 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.utils.translation import gettext_lazy as _ + +import horizon + +from tacker_horizon.openstack_dashboard.dashboards.nfv import dashboard + + +class VnfPMThreshold(horizon.Panel): + name = _("PM Threshold") + slug = "vnfpmthreshold" + + +dashboard.Nfv.register(VnfPMThreshold) diff --git a/tacker_horizon/openstack_dashboard/dashboards/nfv/vnfpmthreshold/tables.py b/tacker_horizon/openstack_dashboard/dashboards/nfv/vnfpmthreshold/tables.py new file mode 100644 index 0000000..14617f0 --- /dev/null +++ b/tacker_horizon/openstack_dashboard/dashboards/nfv/vnfpmthreshold/tables.py @@ -0,0 +1,78 @@ +# Copyright (C) 2024 Fujitsu +# All Rights Reserved. +# +# 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.utils.translation import gettext_lazy as _ +from django.utils.translation import ngettext_lazy + +from horizon import tables +from openstack_dashboard import policy + +from tacker_horizon.openstack_dashboard import api + + +class MyFilterAction(tables.FilterAction): + name = "myfilter" + + +class DeletePMThreshold(policy.PolicyTargetMixin, tables.DeleteAction): + @staticmethod + def action_present(count): + return ngettext_lazy( + "Delete PM Threshold", + "Delete PM Thresholds", + count + ) + + @staticmethod + def action_past(count): + return ngettext_lazy( + "Delete PM Threshold", + "Delete PM Thresholds", + count + ) + + def action(self, request, obj_id): + api.tacker.delete_pm_threshold(request, obj_id) + + +class CreatePMThreshold(tables.LinkAction): + name = "createpmthreshold" + verbose_name = _("Create PM Threshold") + classes = ("ajax-modal",) + icon = "plus" + url = "horizon:nfv:vnfpmthreshold:createpmthreshold" + + +class UpdatePMThreshold(tables.LinkAction): + name = "updatepmthreshold" + verbose_name = _("Update PM Threshold") + url = "horizon:nfv:vnfpmthreshold:updatepmthreshold" + classes = ("ajax-modal",) + + +class VNFPMThresholdTable(tables.DataTable): + id = tables.Column('id', link="horizon:nfv:vnfpmthreshold:detail", + verbose_name=_("ID")) + object_type = tables.Column('object_type', verbose_name=_("Object Type")) + links = tables.Column('links', verbose_name=_("Links")) + + class Meta(object): + name = "pmthreshold" + verbose_name = _("PM Threshold") + pagination_param = 'vnfpmthreshold_marker' + prev_pagination_param = 'prev_vnfpmthreshold_marker' + table_actions = (CreatePMThreshold, DeletePMThreshold, MyFilterAction,) + row_actions = (UpdatePMThreshold, DeletePMThreshold,) diff --git a/tacker_horizon/openstack_dashboard/dashboards/nfv/vnfpmthreshold/tabs.py b/tacker_horizon/openstack_dashboard/dashboards/nfv/vnfpmthreshold/tabs.py new file mode 100644 index 0000000..f6fee94 --- /dev/null +++ b/tacker_horizon/openstack_dashboard/dashboards/nfv/vnfpmthreshold/tabs.py @@ -0,0 +1,108 @@ +# Copyright (C) 2024 Fujitsu +# All Rights Reserved. +# +# 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.utils.translation import gettext_lazy as _ + +from horizon import exceptions +from horizon import tabs +from horizon import utils as horizon_utils + +from tacker_horizon.openstack_dashboard import api +from tacker_horizon.openstack_dashboard.dashboards.nfv.vnfpmthreshold \ + import tables + + +class VNFPMThresholdItem(object): + def __init__(self, threshold_id, links, object_type): + self.id = threshold_id + self.name = threshold_id + self.links = links + self.object_type = object_type + + +class VNFPMThresholdTab(tabs.TableTab): + name = _("VNFPMThreshold Tab") + slug = "vnfpmthreshold_tab" + table_classes = (tables.VNFPMThresholdTable,) + template_name = "horizon/common/_detail_table.html" + preload = False + + def has_prev_data(self, table): + return self._has_prev + + def has_more_data(self, table): + return self._has_more + + def get_pmthreshold_data(self): + try: + self._has_prev = False + page_size = horizon_utils.functions.get_page_size(self.request) + marker = self.request.GET.get("vnfpmthreshold_marker", None) + prev_marker = self.request.GET.get( + "prev_vnfpmthreshold_marker", None) + pmthresholds = api.tacker.list_pm_thresholds(self.request) + + if marker is not None or prev_marker is not None: + for i, threshold in enumerate(pmthresholds): + if threshold["id"] == marker and i < len(pmthresholds) - 1: + pmthresholds = pmthresholds[i + 1:] + self._has_prev = True + break + if threshold["id"] == prev_marker and i > page_size: + pmthresholds = pmthresholds[i - page_size:] + self._has_prev = True + break + + if len(pmthresholds) > page_size: + self._has_more = True + else: + self._has_more = False + + rows = [] + for i, threshold in enumerate(pmthresholds): + if i >= page_size: + break + item = VNFPMThresholdItem(threshold['id'], + threshold['_links'], + threshold['objectType']) + rows.append(item) + return rows + except Exception: + self._has_more = False + error_message = _('Failed to get PM Thresholds.') + exceptions.handle(self.request, error_message) + return [] + + +class VNFPMThresholdTabs(tabs.TabGroup): + slug = "vnfpmthreshold_tabs" + tabs = (VNFPMThresholdTab,) + sticky = True + + +class VNFPMThresholdDetailTab(tabs.Tab): + name = _("VNF PM Threshold") + slug = "vnfpmthreshold_detail_tab" + template_name = "nfv/vnfpmthreshold/pmthreshold_detail.html" + + def get_context_data(self, request): + return {'pmthreshold': self.tab_group.kwargs['pmthreshold']} + + +class VNFPMThresholdDetailTabs(tabs.TabGroup): + slug = "vnfpmthreshold_detail_tabs" + tabs = (VNFPMThresholdDetailTab,) + sticky = True diff --git a/tacker_horizon/openstack_dashboard/dashboards/nfv/vnfpmthreshold/templates/vnfpmthreshold/_create_pmthreshold.html b/tacker_horizon/openstack_dashboard/dashboards/nfv/vnfpmthreshold/templates/vnfpmthreshold/_create_pmthreshold.html new file mode 100644 index 0000000..41446b9 --- /dev/null +++ b/tacker_horizon/openstack_dashboard/dashboards/nfv/vnfpmthreshold/templates/vnfpmthreshold/_create_pmthreshold.html @@ -0,0 +1,9 @@ +{% extends "horizon/common/_modal_form.html" %} +{% load i18n %} + +{% block form_attrs %}enctype="multipart/form-data"{% endblock %} + +{% block modal-body-right %} +

{% trans "Description:" %}

+

{% trans "Create a PM Threshold." %}

+{% endblock %} diff --git a/tacker_horizon/openstack_dashboard/dashboards/nfv/vnfpmthreshold/templates/vnfpmthreshold/_update_pmthreshold.html b/tacker_horizon/openstack_dashboard/dashboards/nfv/vnfpmthreshold/templates/vnfpmthreshold/_update_pmthreshold.html new file mode 100644 index 0000000..4033982 --- /dev/null +++ b/tacker_horizon/openstack_dashboard/dashboards/nfv/vnfpmthreshold/templates/vnfpmthreshold/_update_pmthreshold.html @@ -0,0 +1,9 @@ +{% extends "horizon/common/_modal_form.html" %} +{% load i18n %} + +{% block form_attrs %}enctype="multipart/form-data"{% endblock %} + +{% block modal-body-right %} +

{% trans "Description:" %}

+

{% trans "Update a PM Threshold." %}

+{% endblock %} diff --git a/tacker_horizon/openstack_dashboard/dashboards/nfv/vnfpmthreshold/templates/vnfpmthreshold/create_pmthreshold.html b/tacker_horizon/openstack_dashboard/dashboards/nfv/vnfpmthreshold/templates/vnfpmthreshold/create_pmthreshold.html new file mode 100644 index 0000000..79b8268 --- /dev/null +++ b/tacker_horizon/openstack_dashboard/dashboards/nfv/vnfpmthreshold/templates/vnfpmthreshold/create_pmthreshold.html @@ -0,0 +1,11 @@ +{% extends 'base.html' %} +{% load i18n %} +{% block title %}{% trans "Create PM Threshold" %}{% endblock %} + +{% block page_header %} + {% include "horizon/common/_page_header.html" with title=_("Create PM Threshold") %} +{% endblock page_header %} + +{% block main %} + {% include 'nfv/vnfpmthreshold/_create_pmthreshold.html' %} +{% endblock %} diff --git a/tacker_horizon/openstack_dashboard/dashboards/nfv/vnfpmthreshold/templates/vnfpmthreshold/detail.html b/tacker_horizon/openstack_dashboard/dashboards/nfv/vnfpmthreshold/templates/vnfpmthreshold/detail.html new file mode 100644 index 0000000..9d7168a --- /dev/null +++ b/tacker_horizon/openstack_dashboard/dashboards/nfv/vnfpmthreshold/templates/vnfpmthreshold/detail.html @@ -0,0 +1,15 @@ +{% extends 'base.html' %} +{% load i18n %} +{% block title %}{% trans "PM Threshold Detail" %}{% endblock %} + +{% block page_header %} + {% include "horizon/common/_page_header.html" with title=page_title %} +{% endblock page_header %} + +{% block main %} +
+
+ {{ tab_group.render }} +
+
+{% endblock %} diff --git a/tacker_horizon/openstack_dashboard/dashboards/nfv/vnfpmthreshold/templates/vnfpmthreshold/index.html b/tacker_horizon/openstack_dashboard/dashboards/nfv/vnfpmthreshold/templates/vnfpmthreshold/index.html new file mode 100644 index 0000000..558dfdc --- /dev/null +++ b/tacker_horizon/openstack_dashboard/dashboards/nfv/vnfpmthreshold/templates/vnfpmthreshold/index.html @@ -0,0 +1,15 @@ +{% extends 'base.html' %} +{% load i18n %} +{% block title %}{% trans "PM Threshold" %}{% endblock %} + +{% block page_header %} + {% include "horizon/common/_page_header.html" with title=_("PM Threshold") %} +{% endblock page_header %} + +{% block main %} +
+
+ {{ tab_group.render }} +
+
+{% endblock %} diff --git a/tacker_horizon/openstack_dashboard/dashboards/nfv/vnfpmthreshold/templates/vnfpmthreshold/pmthreshold_detail.html b/tacker_horizon/openstack_dashboard/dashboards/nfv/vnfpmthreshold/templates/vnfpmthreshold/pmthreshold_detail.html new file mode 100644 index 0000000..4d79054 --- /dev/null +++ b/tacker_horizon/openstack_dashboard/dashboards/nfv/vnfpmthreshold/templates/vnfpmthreshold/pmthreshold_detail.html @@ -0,0 +1,20 @@ +{% load i18n %} + +
+
+
{% trans "ID" %}
+
{{ pmthreshold.id }}
+
{% trans "Links" %}
+
{{ pmthreshold.links }}
+
{% trans "Callback Uri" %}
+
{{ pmthreshold.callback_uri }}
+
{% trans "Criteria" %}
+
{{ pmthreshold.criteria }}
+
{% trans "Object Instance ID" %}
+
{{ pmthreshold.object_instance_id }}
+
{% trans "Object Type" %}
+
{{ pmthreshold.object_type }}
+
{% trans "Sub Object Instance IDs" %}
+
{{ pmthreshold.sub_object_instance_ids }}
+
+
diff --git a/tacker_horizon/openstack_dashboard/dashboards/nfv/vnfpmthreshold/templates/vnfpmthreshold/update_pmthreshold.html b/tacker_horizon/openstack_dashboard/dashboards/nfv/vnfpmthreshold/templates/vnfpmthreshold/update_pmthreshold.html new file mode 100644 index 0000000..258f367 --- /dev/null +++ b/tacker_horizon/openstack_dashboard/dashboards/nfv/vnfpmthreshold/templates/vnfpmthreshold/update_pmthreshold.html @@ -0,0 +1,11 @@ +{% extends 'base.html' %} +{% load i18n %} +{% block title %}{% trans "Update PM Threshold" %}{% endblock %} + +{% block page_header %} + {% include "horizon/common/_page_header.html" with title=_("Update PM Threshold") %} +{% endblock page_header %} + +{% block main %} + {% include 'nfv/vnfpmthreshold/_update_pmthreshold.html' %} +{% endblock %} diff --git a/tacker_horizon/openstack_dashboard/dashboards/nfv/vnfpmthreshold/urls.py b/tacker_horizon/openstack_dashboard/dashboards/nfv/vnfpmthreshold/urls.py new file mode 100644 index 0000000..ba2c6c9 --- /dev/null +++ b/tacker_horizon/openstack_dashboard/dashboards/nfv/vnfpmthreshold/urls.py @@ -0,0 +1,31 @@ +# Copyright (C) 2024 Fujitsu +# All Rights Reserved. +# +# 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.urls import re_path + +from tacker_horizon.openstack_dashboard.dashboards.nfv.vnfpmthreshold \ + import views + +urlpatterns = [ + re_path(r'^$', views.IndexView.as_view(), name='index'), + re_path(r'^createpmthreshold', views.CreatePMThresholdView.as_view(), + name='createpmthreshold'), + re_path(r'^(?P[^/]+)/updatepmthreshold$', + views.UpdatePMThresholdView.as_view(), + name='updatepmthreshold'), + re_path(r'^(?P[^/]+)/$', views.DetailView.as_view(), + name='detail'), +] diff --git a/tacker_horizon/openstack_dashboard/dashboards/nfv/vnfpmthreshold/views.py b/tacker_horizon/openstack_dashboard/dashboards/nfv/vnfpmthreshold/views.py new file mode 100644 index 0000000..2eda7f4 --- /dev/null +++ b/tacker_horizon/openstack_dashboard/dashboards/nfv/vnfpmthreshold/views.py @@ -0,0 +1,114 @@ +# Copyright (C) 2024 Fujitsu +# All Rights Reserved. +# +# 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.urls import reverse +from django.urls import reverse_lazy +from django.utils.translation import gettext_lazy as _ + +from horizon import exceptions +from horizon import forms +from horizon import tabs +from horizon.utils import memoized + +from tacker_horizon.openstack_dashboard import api as tacker_api +from tacker_horizon.openstack_dashboard.dashboards.nfv.vnfpmthreshold \ + import forms as project_forms +from tacker_horizon.openstack_dashboard.dashboards.nfv.vnfpmthreshold \ + import tabs as pmthreshold_tabs + + +class IndexView(tabs.TabbedTableView): + # A very simple class-based view... + tab_group_class = pmthreshold_tabs.VNFPMThresholdTabs + template_name = 'nfv/vnfpmthreshold/index.html' + + def get_data(self, request, context, *args, **kwargs): + # Add data to the context here... + return context + + +class CreatePMThresholdView(forms.ModalFormView): + form_class = project_forms.CreatePMThreshold + template_name = 'nfv/vnfpmthreshold/create_pmthreshold.html' + success_url = reverse_lazy("horizon:nfv:vnfpmthreshold:index") + modal_id = "create_pmthreshold_modal" + modal_header = _("Create PM Threshold") + submit_label = _("Create PM Threshold") + submit_url = "horizon:nfv:vnfpmthreshold:createpmthreshold" + + def get_context_data(self, **kwargs): + context = super(CreatePMThresholdView, self).get_context_data(**kwargs) + context['submit_url'] = reverse(self.submit_url) + return context + + +class UpdatePMThresholdView(forms.ModalFormView): + form_class = project_forms.UpdatePMThreshold + template_name = 'nfv/vnfpmthreshold/update_pmthreshold.html' + success_url = reverse_lazy("horizon:nfv:vnfpmthreshold:index") + modal_id = "update_pmthreshold_modal" + modal_header = _("Update PM Threshold") + submit_label = _("Update PM Threshold") + submit_url = "horizon:nfv:vnfpmthreshold:updatepmthreshold" + + def get_context_data(self, **kwargs): + context = super(UpdatePMThresholdView, self).get_context_data(**kwargs) + context['submit_url'] = reverse(self.submit_url, + kwargs={'id': self.kwargs['id']}) + return context + + +class DetailView(tabs.TabView): + tab_group_class = pmthreshold_tabs.VNFPMThresholdDetailTabs + template_name = 'nfv/vnfpmthreshold/detail.html' + redirect_url = 'horizon:nfv:vnfpmthreshold:index' + page_title = _("PM Threshold Detail: {{ id }}") + + def get_context_data(self, **kwargs): + context = super(DetailView, self).get_context_data(**kwargs) + pmthreshold = self.get_data() + context['pmthreshold'] = pmthreshold + context['id'] = kwargs['id'] + context['url'] = reverse(self.redirect_url) + return context + + @memoized.memoized_method + def get_data(self): + threshold_id = self.kwargs['id'] + + try: + pmthreshold = tacker_api.tacker.get_pm_threshold(self.request, + threshold_id) + pmthreshold['links'] = pmthreshold.get('_links', '') + pmthreshold['callback_uri'] = pmthreshold.get('callbackUri', '') + pmthreshold['criteria'] = pmthreshold.get('criteria', '') + pmthreshold['id'] = pmthreshold.get('id', '') + pmthreshold['object_instance_id'] = pmthreshold.get( + 'objectInstanceId', '') + pmthreshold['object_type'] = pmthreshold.get('objectType', '') + pmthreshold['sub_object_instance_ids'] = pmthreshold.get( + 'subObjectInstanceIds', '') + return pmthreshold + except Exception: + redirect = reverse(self.redirect_url) + exceptions.handle(self.request, + _('Failed to get PM Threshold. (id: %s)') % + threshold_id, redirect=redirect) + raise exceptions.Http302(redirect) + + def get_tabs(self, request, *args, **kwargs): + pmthreshold = self.get_data() + return self.tab_group_class(request, pmthreshold=pmthreshold, **kwargs) diff --git a/tacker_horizon/test/integration/horizon.conf b/tacker_horizon/test/integration/horizon.conf index e459183..cd23fa1 100644 --- a/tacker_horizon/test/integration/horizon.conf +++ b/tacker_horizon/test/integration/horizon.conf @@ -1,4 +1,4 @@ [plugin] is_plugin=True plugin_page_path=tacker_horizon.test.integration.pages -plugin_page_structure='{"NFV": {"VNF Management": {"_": ["VNF Catalog", "VNF Manager"]}, "NFV Orchestration": {"_": ["VIM Management", "VNFFG Catalog", "VNFFG Manager", "NS Catalog", "NS Manager"]}}}' +plugin_page_structure='{"NFV": {"(Legacy)VNF Management": {"_": ["VNF Catalog", "VNF Manager"]}, "(Legacy)NFV Orchestration": {"_": ["VIM Management", "VNFFG Catalog", "VNFFG Manager", "NS Catalog", "NS Manager"]}, "VNF Packages v1": {"_": ["VNF Packages"]}, "VNF LCM v2": {"_": ["VNF LCM", "VNF OP OCC", "LCCN Subscription"]}, "VNF FM v1": {"_": ["Alarm", "Subscription"]}, "VNF PM v2": {"_": ["PM Job", "PM Threshold"]}}}' diff --git a/tacker_horizon/test/integration/pages/nfv/vnffm/__init__.py b/tacker_horizon/test/integration/pages/nfv/vnffm/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tacker_horizon/test/integration/pages/nfv/vnffm/vnffmalarmpage.py b/tacker_horizon/test/integration/pages/nfv/vnffm/vnffmalarmpage.py new file mode 100644 index 0000000..5de8da0 --- /dev/null +++ b/tacker_horizon/test/integration/pages/nfv/vnffm/vnffmalarmpage.py @@ -0,0 +1,22 @@ +# Copyright (C) 2024 Fujitsu +# All Rights Reserved. +# +# 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 openstack_dashboard.test.integration_tests.pages import basepage + + +class VnfFmAlarmPage(basepage.BaseNavigationPage): + def __init__(self, driver, conf): + super(VnfFmAlarmPage, self).__init__(driver, conf) + self._page_title = "Alarm" diff --git a/tacker_horizon/test/integration/pages/nfv/vnffm/vnffmsubscriptionpage.py b/tacker_horizon/test/integration/pages/nfv/vnffm/vnffmsubscriptionpage.py new file mode 100644 index 0000000..a78a3d2 --- /dev/null +++ b/tacker_horizon/test/integration/pages/nfv/vnffm/vnffmsubscriptionpage.py @@ -0,0 +1,22 @@ +# Copyright (C) 2024 Fujitsu +# All Rights Reserved. +# +# 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 openstack_dashboard.test.integration_tests.pages import basepage + + +class VnfFmSubscriptionPage(basepage.BaseNavigationPage): + def __init__(self, driver, conf): + super(VnfFmSubscriptionPage, self).__init__(driver, conf) + self._page_title = "Subscription" diff --git a/tacker_horizon/test/integration/pages/nfv/vnflcm/__init__.py b/tacker_horizon/test/integration/pages/nfv/vnflcm/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tacker_horizon/test/integration/pages/nfv/vnflcm/lccnsubscriptionpage.py b/tacker_horizon/test/integration/pages/nfv/vnflcm/lccnsubscriptionpage.py new file mode 100644 index 0000000..75ef93d --- /dev/null +++ b/tacker_horizon/test/integration/pages/nfv/vnflcm/lccnsubscriptionpage.py @@ -0,0 +1,22 @@ +# Copyright (C) 2024 Fujitsu +# All Rights Reserved. +# +# 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 openstack_dashboard.test.integration_tests.pages import basepage + + +class LccnSubscriptionPage(basepage.BaseNavigationPage): + def __init__(self, driver, conf): + super(LccnSubscriptionPage, self).__init__(driver, conf) + self._page_title = "LCCN Subscription" diff --git a/tacker_horizon/test/integration/pages/nfv/vnflcm/vnflcmopoccpage.py b/tacker_horizon/test/integration/pages/nfv/vnflcm/vnflcmopoccpage.py new file mode 100644 index 0000000..c264d18 --- /dev/null +++ b/tacker_horizon/test/integration/pages/nfv/vnflcm/vnflcmopoccpage.py @@ -0,0 +1,22 @@ +# Copyright (C) 2024 Fujitsu +# All Rights Reserved. +# +# 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 openstack_dashboard.test.integration_tests.pages import basepage + + +class VnfLcmOpOccPage(basepage.BaseNavigationPage): + def __init__(self, driver, conf): + super(VnfLcmOpOccPage, self).__init__(driver, conf) + self._page_title = "VNF LCM OP OCC" diff --git a/tacker_horizon/test/integration/pages/nfv/vnflcm/vnflcmpage.py b/tacker_horizon/test/integration/pages/nfv/vnflcm/vnflcmpage.py new file mode 100644 index 0000000..472c628 --- /dev/null +++ b/tacker_horizon/test/integration/pages/nfv/vnflcm/vnflcmpage.py @@ -0,0 +1,22 @@ +# Copyright (C) 2024 Fujitsu +# All Rights Reserved. +# +# 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 openstack_dashboard.test.integration_tests.pages import basepage + + +class VnfLcmPage(basepage.BaseNavigationPage): + def __init__(self, driver, conf): + super(VnfLcmPage, self).__init__(driver, conf) + self._page_title = "VNF LCM" diff --git a/tacker_horizon/test/integration/pages/nfv/vnfpackages/__init__.py b/tacker_horizon/test/integration/pages/nfv/vnfpackages/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tacker_horizon/test/integration/pages/nfv/vnfpackages/vnfpackagespage.py b/tacker_horizon/test/integration/pages/nfv/vnfpackages/vnfpackagespage.py new file mode 100644 index 0000000..9deb04f --- /dev/null +++ b/tacker_horizon/test/integration/pages/nfv/vnfpackages/vnfpackagespage.py @@ -0,0 +1,22 @@ +# Copyright (C) 2024 Fujitsu +# All Rights Reserved. +# +# 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 openstack_dashboard.test.integration_tests.pages import basepage + + +class VnfPackagesPage(basepage.BaseNavigationPage): + def __init__(self, driver, conf): + super(VnfPackagesPage, self).__init__(driver, conf) + self._page_title = "VNF Packages" diff --git a/tacker_horizon/test/integration/pages/nfv/vnfpm/__init__.py b/tacker_horizon/test/integration/pages/nfv/vnfpm/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tacker_horizon/test/integration/pages/nfv/vnfpm/vnfpmjobpage.py b/tacker_horizon/test/integration/pages/nfv/vnfpm/vnfpmjobpage.py new file mode 100644 index 0000000..fb36f5f --- /dev/null +++ b/tacker_horizon/test/integration/pages/nfv/vnfpm/vnfpmjobpage.py @@ -0,0 +1,22 @@ +# Copyright (C) 2024 Fujitsu +# All Rights Reserved. +# +# 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 openstack_dashboard.test.integration_tests.pages import basepage + + +class VnfPmJobPage(basepage.BaseNavigationPage): + def __init__(self, driver, conf): + super(VnfPmJobPage, self).__init__(driver, conf) + self._page_title = "PM Job" diff --git a/tacker_horizon/test/integration/pages/nfv/vnfpm/vnfpmthresholdpage.py b/tacker_horizon/test/integration/pages/nfv/vnfpm/vnfpmthresholdpage.py new file mode 100644 index 0000000..31ea4fb --- /dev/null +++ b/tacker_horizon/test/integration/pages/nfv/vnfpm/vnfpmthresholdpage.py @@ -0,0 +1,22 @@ +# Copyright (C) 2024 Fujitsu +# All Rights Reserved. +# +# 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 openstack_dashboard.test.integration_tests.pages import basepage + + +class VnfPmThresholdPage(basepage.BaseNavigationPage): + def __init__(self, driver, conf): + super(VnfPmThresholdPage, self).__init__(driver, conf) + self._page_title = "PM Threshold" diff --git a/tacker_horizon/test/integration/test_basic.py b/tacker_horizon/test/integration/test_basic.py index d0fd69b..265bff1 100644 --- a/tacker_horizon/test/integration/test_basic.py +++ b/tacker_horizon/test/integration/test_basic.py @@ -53,3 +53,43 @@ class TestTackerDashboardInstalled(helpers.TestCase): ns_manager = self.home_pg.go_to_nfv_nfvorchestration_nsmanagerpage() self.assertEqual(ns_manager.page_title, 'NS Manager - OpenStack Dashboard') + + def test_vnf_packages_page_opened(self): + vnf_packages = self.home_pg.go_to_nfv_vnfpackages_vnfpackagespage() + self.assertEqual(vnf_packages.page_title, + 'VNF Packages - OpenStack Dashboard') + + def test_vnflcm_page_opened(self): + vnflcm = self.home_pg.go_to_nfv_vnflcm_vnflcmpage() + self.assertEqual(vnflcm.page_title, + 'VNF LCM - OpenStack Dashboard') + + def test_vnflcm_opocc_page_opened(self): + vnflcm_opocc = self.home_pg.go_to_nfv_vnflcm_vnflcmopoccpage() + self.assertEqual(vnflcm_opocc.page_title, + 'VNF LCM OP OCC - OpenStack Dashboard') + + def test_vnflcm_subscription_page_opened(self): + vnflcm_subsc = self.home_pg.go_to_nfv_vnflcm_lccnsubscriptionpage() + self.assertEqual(vnflcm_subsc.page_title, + 'LCCN Subscription - OpenStack Dashboard') + + def test_vnffm_alarm_page_opened(self): + fm_alarm = self.home_pg.go_to_nfv_vnffm_vnffmalarmpage() + self.assertEqual(fm_alarm.page_title, + 'Alarm - OpenStack Dashboard') + + def test_vnffm_subscription_page_opened(self): + fm_subsc = self.home_pg.go_to_nfv_vnffm_vnffmsubscriptionpage() + self.assertEqual(fm_subsc.page_title, + 'Subscription - OpenStack Dashboard') + + def test_vnfpm_pmjob_page_opened(self): + pm_job = self.home_pg.go_to_nfv_vnfpm_vnfpmjobpage() + self.assertEqual(pm_job.page_title, + 'PM Job - OpenStack Dashboard') + + def test_vnfpm_pmthreshold_page_opened(self): + pm_threshold = self.home_pg.go_to_nfv_vnfpm_vnfpmthresholdpage() + self.assertEqual(pm_threshold.page_title, + 'PM Threshold - OpenStack Dashboard')