From: Marco Yuen Date: Fri, 21 May 2010 17:03:17 +0000 (-0700) Subject: Initial commit. X-Git-Url: http://git.onelab.eu/?p=sfa-gui.git;a=commitdiff_plain;h=070773be5d006ef8764f81c075fcc3f37a5ba4e7 Initial commit. --- 070773be5d006ef8764f81c075fcc3f37a5ba4e7 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ea1472e --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +output/ diff --git a/Configure.py b/Configure.py new file mode 100644 index 0000000..ef7fd76 --- /dev/null +++ b/Configure.py @@ -0,0 +1,90 @@ +from Sink import Sink, SinkInfo +from pyjamas.ui.Composite import Composite +from pyjamas.ui.RootPanel import RootPanel +from pyjamas.ui.VerticalPanel import VerticalPanel +from pyjamas.ui.HorizontalPanel import HorizontalPanel +from pyjamas.ui.CaptionPanel import CaptionPanel +from pyjamas.ui.TextBox import TextBox +from pyjamas.ui.Button import Button +from pyjamas.ui.HTML import HTML +from pyjamas.ui.Grid import Grid +from pyjamas.ui import HasAlignment +from SfaData import SfaData + +class TopPanel(Composite): + panel = None + + def __init__(self): + TopPanel.panel = self + Composite.__init__(self) + self.data = SfaData() + + self.outer = HorizontalPanel() + self.inner = VerticalPanel() + + self.outer.setHorizontalAlignment(HasAlignment.ALIGN_RIGHT) + self.inner.setHorizontalAlignment(HasAlignment.ALIGN_RIGHT) + + # No links right now... + self.links = HorizontalPanel() + self.links.setSpacing(4) + + self.outer.add(self.inner) + self.refresh() + + self.initWidget(self.outer) + + def onClick(self, sender): + pass + + def refresh(self): + self.inner.clear() + self.inner.add(HTML("User: %s" % self.data.getUser())) + self.inner.add(HTML("Slice: %s" % self.data.getSlice())) + self.inner.add(self.links) + +class LabeledTextBox: + def __init__(self, label, initialText): + self.hp = CaptionPanel(label) + self.tb = TextBox() + self.tb.setWidth("100%") + self.tb.setText(initialText) + self.hp.add(self.tb) + + def getText(self): + return self.tb.getText() + + def getWidget(self): + return self.hp + +class Configure(Sink): + def __init__(self): + Sink.__init__(self) + self.data = SfaData() + + panel = VerticalPanel() + panel.setWidth("100%") + panel.setWidth("100%") + + self.userBox = LabeledTextBox("User HRN:", SfaData.user) + panel.add(self.userBox.getWidget()) + self.sliceBox = LabeledTextBox("Slice HRN:", SfaData.slice) + panel.add(self.sliceBox.getWidget()) + + hp = HorizontalPanel() + applyButton = Button("Apply", self.apply) + hp.add(applyButton) + panel.add(hp) + + self.initWidget(panel) + + def apply(self, sender): + self.data.setUser(self.userBox.getText()) + self.data.setSlice(self.sliceBox.getText()) + TopPanel.panel.refresh() + + def onShow(self): + pass + +def init(): + return SinkInfo("Configure", "Configure SFA Federation GUI", Configure) diff --git a/Identities.py b/Identities.py new file mode 100644 index 0000000..a6a6f39 --- /dev/null +++ b/Identities.py @@ -0,0 +1,17 @@ +from Sink import Sink, SinkInfo +from pyjamas.ui.HTML import HTML + +class Identities(Sink): + def __init__(self): + + Sink.__init__(self) + + text="Not implemented yet" + self.initWidget(HTML(text, True)) + + def onShow(self): + pass + + +def init(): + return SinkInfo("Identities", "Manage SFA Identities", Identities) diff --git a/Info.py b/Info.py new file mode 100644 index 0000000..4fcf72a --- /dev/null +++ b/Info.py @@ -0,0 +1,20 @@ +from Sink import Sink, SinkInfo +from pyjamas.ui.HTML import HTML + +class Info(Sink): + def __init__(self): + + Sink.__init__(self) + + text="
This is the SFA Federation GUI. " + text+="It allows users to manage their SFA identities and slices, " + text+="add resources from various testbeds to their slices, " + text+="and browse the SFA Registry.

" + self.initWidget(HTML(text, True)) + + def onShow(self): + pass + + +def init(): + return SinkInfo("Info", "Introduction to the SFA Federation GUI", Info) diff --git a/OpenCirrus.py b/OpenCirrus.py new file mode 100644 index 0000000..537f20b --- /dev/null +++ b/OpenCirrus.py @@ -0,0 +1,17 @@ +from Sink import Sink, SinkInfo +from pyjamas.ui.HTML import HTML + +class OpenCirrus(Sink): + def __init__(self): + + Sink.__init__(self) + + text="Not implemented yet" + self.initWidget(HTML(text, True)) + + def onShow(self): + pass + + +def init(): + return SinkInfo("OpenCirrus", "Specify OpenCirrus Resources", OpenCirrus) diff --git a/PlanetLab.py b/PlanetLab.py new file mode 100644 index 0000000..50407c7 --- /dev/null +++ b/PlanetLab.py @@ -0,0 +1,31 @@ +from Sink import Sink, SinkInfo +from pyjamas.ui.VerticalPanel import VerticalPanel +from pyjamas.ui.TextArea import TextArea +from pyjamas.ui.HTML import HTML +from SfaData import PlanetLabData + +class PlanetLab(Sink): + def __init__(self): + + Sink.__init__(self) + self.panel = VerticalPanel() + self.panel.setSize("100%", "100%") + self.data = PlanetLabData() + self.rspec = self.data.getRSpec() + + # Just to show that we can retrieve the RSpec + ta = TextArea() + ta.setSize("100%", "100%") + ta.setText(self.rspec) + self.panel.add(ta) + + self.initWidget(self.panel) + + def onShow(self): + # Do we want to refresh the RSpec? + pass + + + +def init(): + return SinkInfo("PlanetLab", "Specify PlanetLab Resources", PlanetLab) diff --git a/SfaBrowser.py b/SfaBrowser.py new file mode 100644 index 0000000..778b448 --- /dev/null +++ b/SfaBrowser.py @@ -0,0 +1,17 @@ +from Sink import Sink, SinkInfo +from pyjamas.ui.HTML import HTML + +class SfaBrowser(Sink): + def __init__(self): + + Sink.__init__(self) + + text="Not implemented yet" + self.initWidget(HTML(text, True)) + + def onShow(self): + pass + + +def init(): + return SinkInfo("Browse SFA", "SFA Hierarchy Browser", SfaBrowser) diff --git a/SfaData.py b/SfaData.py new file mode 100644 index 0000000..a26aaf9 --- /dev/null +++ b/SfaData.py @@ -0,0 +1,68 @@ +import os +from subprocess import call +from sfa.util.rspecHelper import RSpec + +class SfaData: + authority = "plc.princeton" + user = "plc.princeton.acb" + slice = "plc.princeton.iias" + + def __init__(self): + self.registry = None + self.slicemgr = None + + def getAuthority(self): + return SfaData.authority + + def getUser(self): + return SfaData.user + + def setUser(self, user): + SfaData.user = user + + # Should probably get authority from user record instead... + a = SfaData.user.split('.') + SfaData.authority = '.'.join(a[:len(a)-1]) + + def getSlice(self): + return SfaData.slice + + def setSlice(self, slice): + SfaData.slice = slice + + def getRecord(self): + pass + + def getRSpec(self): + slice = self.getSlice() + call(["sfi.py", "-u", self.getUser(), "-a", self.getAuthority(), + "-r", self.registry, "-s", self.slicemgr, "resources", + "-o", slice, slice]) + filename = os.path.expanduser("~/.sfi/" + slice + ".rspec") + f = open(filename, "r") + xml = f.read() + f.close() + return xml + +class ViniData(SfaData): + def __init__(self): + SfaData.__init__(self) + self.registry = "http://www.planet-lab.org:12345" + self.slicemgr = "http://www.vini-veritas.net:12347" + + def getRSpec(self): + xml = SfaData.getRSpec(self) + return RSpec(xml) + +class PlanetLabData(SfaData): + def __init__(self): + SfaData.__init__(self) + self.registry = "http://www.planet-lab.org:12345" + self.slicemgr = "http://www.planet-lab.org:12347" + +class OpenCirrusData(SfaData): + def __init__(self): + SfaData.__init__(self) + self.registry = "http://www.planet-lab.org:12345" + self.slicemgr = "http://www.planet-lab.org:12347" + diff --git a/SfaGUI.py b/SfaGUI.py new file mode 100644 index 0000000..8cd7cc1 --- /dev/null +++ b/SfaGUI.py @@ -0,0 +1,111 @@ +import pyjd # this is dummy in pyjs + +from pyjamas.ui.Button import Button +from pyjamas.ui.RootPanel import RootPanel +from pyjamas.ui.HTML import HTML +from pyjamas.ui.DockPanel import DockPanel +from pyjamas.ui import HasAlignment +from pyjamas.ui.Hyperlink import Hyperlink +from pyjamas.ui.VerticalPanel import VerticalPanel +from pyjamas import Window +from SinkList import SinkList +from Configure import TopPanel +from pyjamas import History +import Info +import Slices +import Identities +import PlanetLab +import VINI +import OpenCirrus +import SfaBrowser +import Configure + +class SfaGUI: + def onHistoryChanged(self, token): + info = self.sink_list.find(token) + if info: + self.show(info, False) + else: + self.showInfo() + + def onModuleLoad(self): + self.tp = TopPanel() + self.tp.setWidth("100%") + + self.curInfo='' + self.curSink=None + self.description=HTML() + self.sink_list=SinkList() + self.panel=DockPanel() + self.loadSinks() + self.sinkContainer = DockPanel() + self.sinkContainer.setStyleName("ks-Sink") + + vp=VerticalPanel() + vp.setWidth("100%") + vp.setHeight("100%") + vp.add(self.description) + vp.add(self.sinkContainer) + + self.description.setStyleName("ks-Info") + + self.panel.add(self.sink_list, DockPanel.WEST) + self.panel.add(vp, DockPanel.CENTER) + + self.panel.setCellVerticalAlignment(self.sink_list, HasAlignment.ALIGN_TOP) + self.panel.setCellWidth(vp, "100%") + self.panel.setCellHeight(vp, "100%") + + History.addHistoryListener(self) + RootPanel().add(self.tp) + RootPanel().add(self.panel) + + #Show the initial screen. + initToken = History.getToken() + if len(initToken): + self.onHistoryChanged(initToken) + else: + self.showInfo() + + def show(self, info, affectHistory): + if info == self.curInfo: return + self.curInfo = info + + if self.curSink <> None: + self.curSink.onHide() + self.sinkContainer.remove(self.curSink) + + self.curSink = info.getInstance() + self.sink_list.setSinkSelection(info.getName()) + self.description.setHTML(info.getDescription()) + + if (affectHistory): + History.newItem(info.getName()) + + self.sinkContainer.add(self.curSink, DockPanel.CENTER) + self.sinkContainer.setCellWidth(self.curSink, "100%") + self.sinkContainer.setCellHeight(self.curSink, "100%") + self.sinkContainer.setCellVerticalAlignment(self.curSink, HasAlignment.ALIGN_TOP) + self.curSink.onShow() + + def loadSinks(self): + self.sink_list.addSink(Info.init()) + self.sink_list.addSink(Identities.init()) + self.sink_list.addSink(Slices.init()) + self.sink_list.addSink(PlanetLab.init()) + self.sink_list.addSink(VINI.init()) + self.sink_list.addSink(OpenCirrus.init()) + self.sink_list.addSink(SfaBrowser.init()) + self.sink_list.addSink(Configure.init()) + + def showInfo(self): + self.show(self.sink_list.find("Info"), False) + + + + +if __name__ == '__main__': + pyjd.setup("public/SfaGUI.html") + app = SfaGUI() + app.onModuleLoad() + pyjd.run() diff --git a/Sink.py b/Sink.py new file mode 100644 index 0000000..8032d90 --- /dev/null +++ b/Sink.py @@ -0,0 +1,36 @@ +from pyjamas.ui.Composite import Composite + +class Sink(Composite): + def __init__(self): + Composite.__init__(self) + + def onHide(self): + pass + + def onShow(self): + pass + + def baseURL(self): + return "" + +class SinkInfo: + def __init__(self, name, desc, object_type): + self.name=name + self.description=desc + self.object_type=object_type + self.instance=None + + def createInstance(self): + return self.object_type() + + def getDescription(self): + return self.description + + def getInstance(self): + if self.instance==None: + self.instance=self.createInstance() + return self.instance + + def getName(self): + return self.name + diff --git a/SinkList.py b/SinkList.py new file mode 100644 index 0000000..129a26f --- /dev/null +++ b/SinkList.py @@ -0,0 +1,41 @@ +from pyjamas.ui.Composite import Composite +from pyjamas.ui.VerticalPanel import VerticalPanel +from pyjamas.ui.Hyperlink import Hyperlink + +class SinkList(Composite): + def __init__(self): + Composite.__init__(self) + + self.vp_list=VerticalPanel() + self.sinks=[] + self.selectedSink=-1 + + self.initWidget(self.vp_list) + self.setStyleName("ks-List") + + def addSink(self, info): + name = info.getName() + link = Hyperlink(name, False, name) + link.setStyleName("ks-SinkItem") + self.vp_list.add(link) + self.sinks.append(info) + + def find(self, sinkName): + for info in self.sinks: + if info.getName()==sinkName: + return info + return None + + def setSinkSelection(self, name): + if self.selectedSink <> -1: + self.vp_list.getWidget(self.selectedSink).removeStyleName("ks-SinkItem-selected") + + for i in range(len(self.sinks)): + info = self.sinks[i] + if (info.getName()==name): + self.selectedSink = i + widget=self.vp_list.getWidget(self.selectedSink) + widget.addStyleName("ks-SinkItem-selected") + return + + diff --git a/Slices.py b/Slices.py new file mode 100644 index 0000000..706e8b7 --- /dev/null +++ b/Slices.py @@ -0,0 +1,17 @@ +from Sink import Sink, SinkInfo +from pyjamas.ui.HTML import HTML + +class Slices(Sink): + def __init__(self): + + Sink.__init__(self) + + text="Not implemented yet" + self.initWidget(HTML(text, True)) + + def onShow(self): + pass + + +def init(): + return SinkInfo("Slices", "Manage SFA Slices", Slices) diff --git a/VINI.py b/VINI.py new file mode 100644 index 0000000..c82c7fd --- /dev/null +++ b/VINI.py @@ -0,0 +1,46 @@ +from Sink import Sink, SinkInfo +from pyjamas.ui.horizsplitpanel import HorizontalSplitPanel +from pyjamas.ui.CaptionPanel import CaptionPanel +from pyjamas.ui.ListBox import ListBox +from pyjamas.ui.HTML import HTML +from SfaData import ViniData + +class VINI(Sink): + def __init__(self): + + Sink.__init__(self) + self.panel = HorizontalSplitPanel() + self.panel.setSize("100%", "100%") + self.panel.setSplitPosition("50%") + self.data = ViniData() + self.rspec = self.data.getRSpec() + + leftcap = CaptionPanel("Available nodes") + leftcap.setSize("90%", "90%") + leftlist = ListBox(MultipleSelect=True) + leftlist.setSize("100%", "100%") + available = self.rspec.get_node_list() + for i in available: + leftlist.addItem(i) + leftcap.add(leftlist) + + rightcap = CaptionPanel("Selected nodes") + rightcap.setSize("90%", "90%") + rightlist = ListBox(MultipleSelect=True) + rightlist.setSize("100%", "100%") + slivers = self.rspec.get_sliver_list() + for i in slivers: + rightlist.addItem(i) + rightcap.add(rightlist) + + self.panel.setLeftWidget(leftcap) + self.panel.setRightWidget(rightcap) + + self.initWidget(self.panel) + + def onShow(self): + pass + + +def init(): + return SinkInfo("VINI", "Specify VINI Resources", VINI) diff --git a/build.sh b/build.sh new file mode 100755 index 0000000..b29cee9 --- /dev/null +++ b/build.sh @@ -0,0 +1,2 @@ +#!/bin/sh +../../bin/pyjsbuild $@ SfaGUI diff --git a/public/SfaGUI.css b/public/SfaGUI.css new file mode 100644 index 0000000..5c3f69c --- /dev/null +++ b/public/SfaGUI.css @@ -0,0 +1,292 @@ +body { + background-color: white; + color: black; + font-family: Arial, sans-serif; + font-size: smaller; + margin: 20px 20px 20px 20px; +} + +code { + font-size: small; +} + +a { + color: darkblue; +} + +a:visited { + color: darkblue; +} + +.gwt-BorderedPanel { +} + +.gwt-Button { +} + +.gwt-Canvas { +} + +.gwt-CheckBox { + font-size: smaller; +} + +.gwt-DialogBox { + sborder: 8px solid #C3D9FF; + border: 2px outset; + background-color: white; +} + +.gwt-DialogBox .Caption { + background-color: #C3D9FF; + padding: 3px; + margin: 2px; + font-weight: bold; + cursor: default; +} + +.gwt-FileUpload { +} + +.gwt-Frame { +} + +.gwt-HorizontalSplitter .Bar { + width: 8px; + background-color: #C3D9FF; +} + +.gwt-VerticalSplitter .Bar { + height: 8px; + background-color: #C3D9FF; +} + +.gwt-HTML { + font-size: smaller; +} + +.gwt-Hyperlink { +} + +.gwt-Image { +} + +.gwt-Label { + font-size: smaller; +} + +.gwt-ListBox { +} + +.gwt-MenuBar { + background-color: #C3D9FF; + border: 1px solid #87B3FF; + cursor: default; +} + +.gwt-MenuBar .gwt-MenuItem { + padding: 1px 4px 1px 4px; + font-size: smaller; + cursor: default; +} + +.gwt-MenuBar .gwt-MenuItem-selected { + background-color: #E8EEF7; +} + +.gwt-PasswordTextBox { +} + +.gwt-RadioButton { + font-size: smaller; +} + +.gwt-TabPanel { +} + +.gwt-TabPanelBottom { + border-left: 1px solid #87B3FF; +} + +.gwt-TabBar { + background-color: #C3D9FF; + font-size: smaller; +} + +.gwt-TabBar .gwt-TabBarFirst { + height: 100%; + border-bottom: 1px solid #87B3FF; + padding-left: 3px; +} + +.gwt-TabBar .gwt-TabBarRest { + border-bottom: 1px solid #87B3FF; + padding-right: 3px; +} + +.gwt-TabBar .gwt-TabBarItem { + border-top: 1px solid #C3D9FF; + border-bottom: 1px solid #87B3FF; + padding: 2px; + cursor: pointer; + cursor: hand; +} + +.gwt-TabBar .gwt-TabBarItem-selected { + font-weight: bold; + background-color: #E8EEF7; + border-top: 1px solid #87B3FF; + border-left: 1px solid #87B3FF; + border-right: 1px solid #87B3FF; + border-bottom: 1px solid #E8EEF7; + padding: 2px; + cursor: default; +} + +.gwt-TextArea { +} + +.gwt-TextBox { +} + +.gwt-Tree { +} + +.gwt-Tree .gwt-TreeItem { + font-size: smaller; +} + +.gwt-Tree .gwt-TreeItem-selected { + background-color: #C3D9FF; +} + +.gwt-StackPanel { +} + +.gwt-StackPanel .gwt-StackPanelItem { + background-color: #C3D9FF; + cursor: pointer; + cursor: hand; +} + +.gwt-StackPanel .gwt-StackPanelItem-selected { +} + +/* -------------------------------------------------------------------------- */ +.ks-Sink { + border: 8px solid #C3D9FF; + background-color: #E8EEF7; + width: 100%; + height: 24em; +} + +.ks-Info { + background-color: #C3D9FF; + padding: 10px 10px 2px 10px; + font-size: smaller; +} + +.ks-List { + margin-top: 8px; + margin-bottom: 8px; + font-size: smaller; +} + +.ks-List .ks-SinkItem { + width: 100%; + padding: 0.3em; + padding-right: 16px; + cursor: pointer; + cursor: hand; +} + +.ks-List .ks-SinkItem-selected { + background-color: #C3D9FF; +} + +.ks-images-Image { + margin: 8px; +} + +.ks-images-Button { + margin: 8px; + cursor: pointer; + cursor: hand; +} + +.ks-layouts { + margin: 8px; +} + +.ks-layouts-Label { + background-color: #C3D9FF; + font-weight: bold; + margin-top: 1em; + padding: 2px 0px 2px 0px; + width: 100%; +} + +.ks-layouts-Scroller { + height: 128px; + border: 2px solid #C3D9FF; + padding: 8px; + margin: 8px; +} + +.ks-popups-Popup { + background-color: white; + border: 1px solid #87B3FF; + padding: 4px; +} + +.infoProse { + margin: 8px; +} + +/****************************************************** +* DisclosurePanel +******************************************************/ +.gwt-DisclosurePanel +{ + background-color : #ddf; + border : 1px solid #009; +} +.gwt-DisclosurePanel .header +{ + font-size : 70%; + cursor : pointer; + cursor : hand; +} +.gwt-DisclosurePanel-closed +{ +} +.gwt-DisclosurePanel-closed .header +{ +} +.gwt-DisclosurePanel-open +{ +} +.gwt-DisclosurePanel-open .header +{ +} +.gwt-DisclosurePanel-open .content +{ +} + +.gwt-HorizontalSplitPanel { +} + +.gwt-HorizontalSplitPanel .hsplitter { + cursor: move; + border: 0px; + background: #91c0ef url(images/vborder.png) repeat-x; +} + +.gwt-VerticalSplitPanel { +} + +.gwt-VerticalSplitPanel .vsplitter { + cursor: move; + border: 0px; + background: #91c0ef url(images/hborder.png) repeat-x; +} diff --git a/public/SfaGUI.html b/public/SfaGUI.html new file mode 100644 index 0000000..d50c334 --- /dev/null +++ b/public/SfaGUI.html @@ -0,0 +1,13 @@ + + + + + SFA Federation GUI + + + + + + + + diff --git a/public/SplitPanel.css b/public/SplitPanel.css new file mode 100644 index 0000000..4706eb7 --- /dev/null +++ b/public/SplitPanel.css @@ -0,0 +1,16 @@ +.gwt-HorizontalSplitPanel { +} + +.gwt-HorizontalSplitPanel .hsplitter { + cursor: move; + border: 0px; + background: #91c0ef url(images/vborder.png) repeat-x; +} +.gwt-VerticalSplitPanel { +} + +.gwt-VerticalSplitPanel .vsplitter { + cursor: move; + border: 0px; + background: #91c0ef url(images/hborder.png) repeat-x; +} diff --git a/public/images/hborder.png b/public/images/hborder.png new file mode 100644 index 0000000..509eb1b Binary files /dev/null and b/public/images/hborder.png differ diff --git a/public/images/vborder.png b/public/images/vborder.png new file mode 100644 index 0000000..435b816 Binary files /dev/null and b/public/images/vborder.png differ diff --git a/public/splitPanelThumb.png b/public/splitPanelThumb.png new file mode 100644 index 0000000..d2ef025 Binary files /dev/null and b/public/splitPanelThumb.png differ