Initial Murano support

First stab at Murano support. Supports plugin autodetection,
importing Packages and Bundles, installation detection
and launch support.

Change-Id: I70a268879e48a9d7e6c7cc6b5011a5d1e4710765
This commit is contained in:
Kevin Fox 2015-08-28 05:57:01 -07:00
parent 0d4bc3a00a
commit 60b8f3ada0
7 changed files with 93 additions and 12 deletions

View File

@ -20,8 +20,6 @@ How to try this package
git clone http://github.com/openstack/horizon.git
git clone http://github.com/stackforge/apps-catalog-ui.git
cd horizon
git fetch https://review.openstack.org/openstack/horizon refs/changes/73/206773/23 && git checkout FETCH_HEAD
git fetch https://review.openstack.org/openstack/horizon refs/changes/43/216443/1 && git cherry-pick FETCH_HEAD
./run_tests.sh -f --docs
cp ./openstack_dashboard/local/local_settings.py.example ./openstack_dashboard/local/local_settings.py
pushd ../apps-catalog-ui
@ -30,3 +28,5 @@ How to try this package
popd
./run_tests.sh --runserver 127.0.0.1:18000
* For Murano support, you need to patch the murano-dashboard plugin with:
https://review.openstack.org/#/c/217747/

View File

@ -8,10 +8,27 @@
<div ng-switch-when="glance" ng-switch="asset.attributes.indirect_url || '_undefined_'">
<a ng-switch-default ng-class="extraclasses" class="btn btn-primary btn-default" target="_blank" href="{$ asset.attributes.indirect_url $}">Install Instructions</a>
<div ng-switch-when="_undefined_" ng-switch="asset.installed">
<a ng-switch-default ng-class="extraclasses" class="btn btn-default disabled ajax-modal" href="/}">Checking</a>
<a ng-switch-default ng-class="extraclasses" class="btn btn-default disabled ajax-modal" href="/">Checking</a>
<a ng-switch-when="false" ng-class="extraclasses" class="btn btn-primary btn-default ajax-modal" href="/project/images/create/?name={$ asset.name | encodeURIComponent $}&source_type=url&image_url={$ asset.attributes.url | encodeURIComponent $}&disk_format={$ asset.service.disk_format $}&architecture={$ asset.service.architecture $}&minimum_disk={$ asset.service.min_disk $}&minimum_ram={$ asset.service.min_ram $}&description={$ asset.description $}">Install</a>
<a ng-switch-when="true" ng-class="extraclasses" class="btn btn-success btn-default ajax-modal" href="/project/instances/launch?source_type=image_id&source_id={$ asset.installed_id $}">Launch</a>
</a>
</div>
</div>
<div ng-switch-when="murano" ng-switch="asset.has_murano">
<a ng-switch-when="false" ng-class="extraclasses" class="btn btn-default disabled ajax-modal" href="/">Unsupported</a>
<div ng-switch-when="true" ng-switch="asset.installed">
<a ng-switch-default ng-class="extraclasses" class="btn btn-default disabled ajax-modal" href="/">Checking</a>
<a ng-switch-when="false" ng-class="extraclasses" class="btn btn-primary btn-default ajax-modal" href="/murano/packages/upload?repo_name={$ asset.service.package_name | encodeURIComponent $}&import_type=by_name">Install</a>
<a ng-switch-when="true" ng-class="extraclasses" class="btn btn-success btn-default ajax-modal" href="/murano/catalog/quick-add/{$ asset.service.murano_id $}">Launch</a>
</div>
</div>
<div ng-switch-when="bundle" ng-switch="asset.service.murano_package_name || '_undefined_'">
<div ng-switch-when="_undefined_"></div>
<div ng-switch-default ng-switch="asset.has_murano">
<a ng-switch-when="false" ng-class="extraclasses" class="btn btn-default disabled ajax-modal" href="/">Unsupported</a>
<div ng-switch-when="true" ng-switch="asset.installed || '_undefined_'">
<a ng-switch-default ng-class="extraclasses" class="btn btn-primary btn-default ajax-modal" href="/murano/packages/import_bundle?name={$ asset.service.murano_package_name | encodeURIComponent $}&import_type=by_repo">Install</a>
<a ng-switch-when="true" ng-class="extraclasses" class="btn btn-default disabled ajax-modal" href="/">Installed</a>
</div>
</div>
</div>

View File

@ -40,18 +40,20 @@
'$scope',
'$http',
'$timeout',
'$modal',
'horizon.framework.widgets.toast.service',
'appCatalogModel',
appComponentCatalogTableCtrl
]).service('appCatalogModel', [
'$http',
'$injector',
'horizon.app.core.openstack-service-api.heat',
'horizon.app.core.openstack-service-api.glance',
'horizon.app.core.openstack-service-api.serviceCatalog',
appCatalogModel
]).directive('stars', stars);
function appCatalogModel($http, heatAPI, glanceAPI, serviceCatalog) {
function appCatalogModel($http, $injector, heatAPI, glanceAPI, serviceCatalog) {
var $scope = this;
var callbacks = {
update: [],
@ -97,6 +99,12 @@
callback();
});
};
var muranoAPI;
$scope.has_murano = false;
if ($injector.has('horizon.app.core.openstack-service-api.murano')) {
muranoAPI = $injector.get('horizon.app.core.openstack-service-api.murano');
$scope.has_murano = true;
}
//FIXME [{'name':'heat', 'type':'orchestration'}, {'name':'glance', 'type':'image'}]
serviceCatalog.get().then(function(catalog){
angular.forEach(catalog, function(entry){
@ -156,11 +164,12 @@
this.register_callback = function(type, callback) {
callbacks[type].push(callback);
};
this.init = function(app_catalog_url) {
this.init = function(app_catalog_settings) {
var app_catalog_url = app_catalog_settings.APP_CATALOG_URL;
var req = {
url: app_catalog_url + '/api/v1/assets',
headers: {'X-Requested-With': undefined}
}
};
$http(req).success(function(data) {
if('deprecated' in data) {
notify_deprecated(data['deprecated']);
@ -187,7 +196,10 @@
}
if (asset.service.type == 'heat') {
process(asset);
} else if (asset.service.type == 'murano') {
asset.validated = true;
}
asset.has_murano = $scope.has_murano;
}
$scope.glance_loaded = true;
$scope.murano_loaded = true;
@ -195,6 +207,21 @@
}).error(function() {
notify_error('There was an error while retrieving entries from the Application Catalog.');
});
if($scope.has_murano) {
muranoAPI.getPackages().success(function(data) {
$scope.murano_packages = data;
var murano_package_definitions = {};
for (var p in data.packages){
var definitions = data.packages[p]['class_definitions'];
for (var d in definitions) {
var definition = definitions[d];
murano_package_definitions[definition] = {'id': data.packages[p]['id']};
}
}
$scope.murano_package_definitions = murano_package_definitions;
update_found_assets($scope);
});
}
glanceAPI.getImages().success(function(data) {
$scope.glance_images = data;
var glance_names = {}
@ -300,7 +327,7 @@
};
}
function appComponentCatalogTableCtrl($scope, $http, $timeout, toast, appCatalogModel) {
function appComponentCatalogTableCtrl($scope, $http, $timeout, $modal, toast, appCatalogModel) {
$scope.assets = appCatalogModel.assets_filtered
var update = function(){
$timeout(function() {
@ -308,7 +335,7 @@
}, 0, false);
};
appCatalogModel.register_callback('update', update);
common_init($scope, toast, appCatalogModel);
common_init($scope, $modal, toast, appCatalogModel);
$scope.switcher = {pannel: 'component', active: 'list'};
}
@ -326,6 +353,19 @@
}
}
}
if('murano_loaded' in $scope && 'murano_package_definitions' in $scope){
for (var i in $scope.assets){
if($scope.assets[i].service.type != 'murano'){
continue;
}
var definition = $scope.assets[i].service.package_name;
var is_installed = definition in ($scope.murano_package_definitions);
$scope.assets[i].installed = is_installed;
if(is_installed) {
$scope.assets[i].service.murano_id = $scope.murano_package_definitions[definition]['id'];
}
}
}
var asset_name_to_asset = {};
angular.forEach($scope.assets, function(asset){
asset_name_to_asset[asset.name] = asset;

View File

@ -9,7 +9,7 @@
{% block main %}
<div ng-cloak
ng-controller="appCatalogTableCtrl" ng-init="init('{{ APP_CATALOG_URL }}')">
ng-controller="appCatalogTableCtrl" ng-init='init({{ APP_CATALOG_SETTINGS }})'>
<ng-include src="'{{ STATIC_URL }}dashboard/project/app_catalog/main_panel.html'"></ng-include>
</div>
{% endblock %}

View File

@ -1,11 +1,23 @@
from horizon import Horizon
from horizon import views
from django.conf import settings
import json
class IndexView(views.APIView):
# A very simple class-based view...
template_name = 'app_catalog/index.html'
def get_data(self, request, context, *args, **kwargs):
context['APP_CATALOG_URL'] = getattr(settings, 'APP_CATALOG_URL', 'http://apps.openstack.org')
has_murano = False
try:
Horizon.get_dashboard('murano')
has_murano = True
except:
pass
app_catalog_settings = {
'HAS_MURANO': has_murano,
'APP_CATALOG_URL': getattr(settings, 'APP_CATALOG_URL', 'http://apps.openstack.org')
}
context['APP_CATALOG_SETTINGS'] = json.dumps(app_catalog_settings)
return context

View File

@ -9,7 +9,7 @@
{% block main %}
<div ng-cloak
ng-controller="appComponentCatalogTableCtrl" ng-init="init('{{ APP_CATALOG_URL }}')">
ng-controller="appComponentCatalogTableCtrl" ng-init="init({{ APP_CATALOG_SETTINGS }})">
<ng-include src="'{{ STATIC_URL }}dashboard/project/app_catalog/main_panel.html'"></ng-include>
</div>
{% endblock %}

View File

@ -1,12 +1,24 @@
from horizon import Horizon
from horizon import views
from django.conf import settings
import json
class IndexView(views.APIView):
# A very simple class-based view...
template_name = 'component_catalog/index.html'
def get_data(self, request, context, *args, **kwargs):
context['APP_CATALOG_URL'] = getattr(settings, 'APP_CATALOG_URL', 'http://apps.openstack.org')
has_murano = False
try:
Horizon.get_dashboard('murano')
has_murano = True
except:
pass
app_catalog_settings = {
'HAS_MURANO': has_murano,
'APP_CATALOG_URL': getattr(settings, 'APP_CATALOG_URL', 'http://apps.openstack.org')
}
context['APP_CATALOG_SETTINGS'] = json.dumps(app_catalog_settings)
return context