X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=planetstack%2Fcore%2Fadmin.py;h=dddebbf95f8eadc0249d95297a227d1d92ac17a7;hb=f7f79a16d519b0ffeb663b267d77d118c444bfc2;hp=8bf797650cad5a5acec9d790ff1ab5b3c237458d;hpb=3b678740bc0feedb21870b06fbc15a5dfb0ffeec;p=plstackapi.git diff --git a/planetstack/core/admin.py b/planetstack/core/admin.py index 8bf7976..dddebbf 100644 --- a/planetstack/core/admin.py +++ b/planetstack/core/admin.py @@ -234,6 +234,13 @@ class SliverInline(PlStackTabularInline): def formfield_for_foreignkey(self, db_field, request=None, **kwargs): if db_field.name == 'deploymentNetwork': kwargs['queryset'] = Deployment.select_by_acl(request.user) + # the inscrutable jquery selector below says: + # find the closest parent "tr" to the current element + # then find the child with class "field-node" + # then find the child with that is a select + # then return its id + kwargs['widget'] = forms.Select(attrs={'onChange': "update_nodes(this, $($(this).closest('tr')[0]).find('.field-node select')[0].id)"}) + #kwargs['widget'] = forms.Select(attrs={'onChange': "console.log($($($(this).closest('tr')[0]).children('.field-node')[0]).children('select')[0].id);"}) field = super(SliverInline, self).formfield_for_foreignkey(db_field, request, **kwargs) @@ -432,14 +439,22 @@ class SliceNetworkInline(PlStackTabularInline): suit_classes = 'suit-tab suit-tab-slicenetworks' fields = ['network'] +class ImageDeploymentsROInline(ReadOnlyTabularInline): + model = ImageDeployments + extra = 0 + verbose_name = "Image Deployments" + verbose_name_plural = "Image Deployments" + suit_classes = 'suit-tab suit-tab-imagedeployments' + fields = ['image', 'deployment', 'glance_image_id'] + class ImageDeploymentsInline(PlStackTabularInline): model = ImageDeployments extra = 0 verbose_name = "Image Deployments" verbose_name_plural = "Image Deployments" suit_classes = 'suit-tab suit-tab-imagedeployments' - fields = ['deployment', 'glance_image_id'] - readonly_fields = ['deployment', 'glance_image_id'] + fields = ['image', 'deployment', 'glance_image_id'] + readonly_fields = ['glance_image_id'] class PlainTextWidget(forms.HiddenInput): input_type = 'hidden' @@ -478,10 +493,19 @@ class DeploymentAdminForm(forms.ModelForm): sites = forms.ModelMultipleChoiceField( queryset=Site.objects.all(), required=False, + help_text="Select which sites are allowed to host nodes in this deployment", widget=FilteredSelectMultiple( verbose_name=('Sites'), is_stacked=False ) ) + images = forms.ModelMultipleChoiceField( + queryset=Image.objects.all(), + required=False, + help_text="Select which images should be deployed on this deployment", + widget=FilteredSelectMultiple( + verbose_name=('Images'), is_stacked=False + ) + ) class Meta: model = Deployment @@ -493,6 +517,42 @@ class DeploymentAdminForm(forms.ModelForm): if self.instance and self.instance.pk: self.fields['sites'].initial = [x.site for x in self.instance.sitedeployments_set.all()] + self.fields['images'].initial = [x.image for x in self.instance.imagedeployments_set.all()] + + def manipulate_m2m_objs(self, this_obj, selected_objs, all_relations, relation_class, local_attrname, foreign_attrname): + """ helper function for handling m2m relations from the MultipleChoiceField + + this_obj: the source object we want to link from + + selected_objs: a list of destination objects we want to link to + + all_relations: the full set of relations involving this_obj, including ones we don't want + + relation_class: the class that implements the relation from source to dest + + local_attrname: field name representing this_obj in relation_class + + foreign_attrname: field name representing selected_objs in relation_class + + This function will remove all newobjclass relations from this_obj + that are not contained in selected_objs, and add any relations that + are in selected_objs but don't exist in the data model yet. + """ + + existing_dest_objs = [] + for relation in list(all_relations): + if getattr(relation, foreign_attrname) not in selected_objs: + #print "deleting site", sdp.site + relation.delete() + else: + existing_dest_objs.append(getattr(relation, foreign_attrname)) + + for dest_obj in selected_objs: + if dest_obj not in existing_dest_objs: + #print "adding site", site + kwargs = {foreign_attrname: dest_obj, local_attrname: this_obj} + relation = relation_class(**kwargs) + relation.save() def save(self, commit=True): deployment = super(DeploymentAdminForm, self).save(commit=False) @@ -505,21 +565,8 @@ class DeploymentAdminForm(forms.ModelForm): # create/destroy the through models ourselves. There has to be # a better way... - sites = self.cleaned_data['sites'] - - existing_sites = [] - for sdp in list(deployment.sitedeployments_set.all()): - if sdp.site not in sites: - #print "deleting site", sdp.site - sdp.delete() - else: - existing_sites.append(sdp.site) - - for site in sites: - if site not in existing_sites: - #print "adding site", site - sdp = SiteDeployments(site=site, deployment=deployment) - sdp.save() + self.manipulate_m2m_objs(deployment, self.cleaned_data['sites'], deployment.sitedeployments_set.all(), SiteDeployments, "deployment", "site") + self.manipulate_m2m_objs(deployment, self.cleaned_data['images'], deployment.imagedeployments_set.all(), ImageDeployments, "deployment", "image") self.save_m2m() @@ -536,14 +583,14 @@ class SiteAssocInline(PlStackTabularInline): class DeploymentAdmin(PlanetStackBaseAdmin): model = Deployment - fieldList = ['name','sites', 'accessControl'] + fieldList = ['name','sites', 'images', 'accessControl'] fieldsets = [(None, {'fields': fieldList, 'classes':['suit-tab suit-tab-sites']})] - inlines = [DeploymentPrivilegeInline,NodeInline,TagInline] + inlines = [DeploymentPrivilegeInline,NodeInline,TagInline] # ,ImageDeploymentsInline] - user_readonly_inlines = [DeploymentPrivilegeROInline,NodeROInline,TagROInline] + user_readonly_inlines = [DeploymentPrivilegeROInline,NodeROInline,TagROInline] # ,ImageDeploymentsROInline] user_readonly_fields = ['name'] - suit_form_tabs =(('sites','Deployment Details'),('nodes','Nodes'),('deploymentprivileges','Privileges'),('tags','Tags')) + suit_form_tabs =(('sites','Deployment Details'),('nodes','Nodes'),('deploymentprivileges','Privileges'),('tags','Tags')) # ,('imagedeployments','Images')) def get_form(self, request, obj=None, **kwargs): if request.user.isReadOnlyUser(): @@ -720,6 +767,19 @@ class SliceAdmin(PlanetStackBaseAdmin): ('reservations','Reservations'), ) + def render_change_form(self, request, context, add=False, change=False, form_url='', obj=None): + #deployment_nodes = {} + #for node in Node.objects.all(): + # deployment_nodes[node.deployment.id] = get(deployment_nodes, node.deployment.id, []).append( (node.id, node.name) ) + + deployment_nodes = [] + for node in Node.objects.all(): + deployment_nodes.append( (node.deployment.id, node.id, node.name) ) + + context["deployment_nodes"] = deployment_nodes + + return super(SliceAdmin, self).render_change_form(request, context, add, change, form_url, obj) + def formfield_for_foreignkey(self, db_field, request, **kwargs): if db_field.name == 'site': kwargs['queryset'] = Site.select_by_user(request.user) @@ -766,14 +826,14 @@ class SlicePrivilegeAdmin(PlanetStackBaseAdmin): def save_model(self, request, obj, form, change): # update openstack connection to use this site/tenant auth = request.session.get('auth', {}) - auth['tenant'] = obj.slice.name + auth['tenant'] = obj.slice.slicename obj.os_manager = OpenStackManager(auth=auth, caller=request.user) obj.save() def delete_model(self, request, obj): # update openstack connection to use this site/tenant auth = request.session.get('auth', {}) - auth['tenant'] = obj.slice.name + auth['tenant'] = obj.slice.slicename obj.os_manager = OpenStackManager(auth=auth, caller=request.user) obj.delete() @@ -788,9 +848,9 @@ class ImageAdmin(PlanetStackBaseAdmin): suit_form_tabs =(('general','Image Details'),('slivers','Slivers'),('imagedeployments','Deployments')) inlines = [SliverInline, ImageDeploymentsInline] - + user_readonly_fields = ['name', 'disk_format', 'container_format'] - user_readonly_inlines = [SliverROInline] + user_readonly_inlines = [SliverROInline, ImageDeploymentsROInline] class NodeForm(forms.ModelForm): class Meta: