Commit 28a36f5d by Szeberényi Imre

Merge branch 'ci-integration' into 'master'

Ci integration

See merge request !9
parents 4d9aea69 a02eb6f2
...@@ -45,4 +45,5 @@ jsi18n ...@@ -45,4 +45,5 @@ jsi18n
scripts.rc scripts.rc
# less # less
*.css # *.css
circle/dashboard/static/compile_bootstrap.css
...@@ -196,7 +196,9 @@ PIPELINE = { ...@@ -196,7 +196,9 @@ PIPELINE = {
"template.less", "template.less",
"dashboard/dashboard.less", "dashboard/dashboard.less",
"network/network.less", "network/network.less",
"autocomplete_light/vendor/select2/dist/css/select2.css", # "autocomplete_light/vendor/select2/dist/css/select2.css",
"admin/css/vendor/select2/select2.css",
"admin/css/autocomplete.css",
"autocomplete_light/select2.css", "autocomplete_light/select2.css",
), ),
"output_filename": "all.css", "output_filename": "all.css",
...@@ -212,10 +214,13 @@ PIPELINE = { ...@@ -212,10 +214,13 @@ PIPELINE = {
"jquery-simple-slider/js/simple-slider.js", "jquery-simple-slider/js/simple-slider.js",
"favico.js/favico.js", "favico.js/favico.js",
"datatables/media/js/jquery.dataTables.js", "datatables/media/js/jquery.dataTables.js",
"autocomplete_light/jquery.init.js", # "autocomplete_light/jquery.init.js",
"autocomplete_light/autocomplete.init.js", # "autocomplete_light/autocomplete.init.js",
"autocomplete_light/vendor/select2/dist/js/select2.js", # "autocomplete_light/vendor/select2/dist/js/select2.js",
"admin/js/vendor/select2/select2.full.js",
"autocomplete_light/autocomplete_light.js",
"autocomplete_light/select2.js", "autocomplete_light/select2.js",
"autocomplete_light/i18n/en.js",
"dashboard/dashboard.js", "dashboard/dashboard.js",
"dashboard/activity.js", "dashboard/activity.js",
"dashboard/group-details.js", "dashboard/group-details.js",
...@@ -239,6 +244,8 @@ PIPELINE = { ...@@ -239,6 +244,8 @@ PIPELINE = {
"output_filename": "all.js", "output_filename": "all.js",
}, },
"vm-detail": {"source_filenames": ( "vm-detail": {"source_filenames": (
"dashboard/js/ace/ace.js",
"dashboard/js/ace/mode-yaml.js",
"clipboard/dist/clipboard.min.js", "clipboard/dist/clipboard.min.js",
"dashboard/vm-details.js", "dashboard/vm-details.js",
"no-vnc/include/util.js", "no-vnc/include/util.js",
...@@ -568,7 +575,7 @@ except: ...@@ -568,7 +575,7 @@ except:
AGENT_VERSION = None AGENT_VERSION = None
LOCALE_PATHS = (join(SITE_ROOT, 'locale'), ) LOCALE_PATHS = (join(SITE_ROOT, 'locale'), )
COMPANY_NAME = get_env_variable("COMPANY_NAME", "BME IK 2015") COMPANY_NAME = get_env_variable("COMPANY_NAME", "BME IK 2022")
first, last = get_env_variable( first, last = get_env_variable(
'VNC_PORT_RANGE', '50000, 60000').replace(' ', '').split(',') 'VNC_PORT_RANGE', '50000, 60000').replace(' ', '').split(',')
......
...@@ -60,7 +60,7 @@ from vm.models import ( ...@@ -60,7 +60,7 @@ from vm.models import (
InstanceTemplate, Lease, InterfaceTemplate, Node, Trait, Instance InstanceTemplate, Lease, InterfaceTemplate, Node, Trait, Instance
) )
from .models import Profile, GroupProfile, Message from .models import Profile, GroupProfile, Message
from .validators import domain_validator from .validators import domain_validator, meta_data_validator, user_data_validator
LANGUAGES_WITH_CODE = ((l[0], ugettext_lazy(l[1], " (", l[0], ")")) LANGUAGES_WITH_CODE = ((l[0], ugettext_lazy(l[1], " (", l[0], ")"))
for l in LANGUAGES) for l in LANGUAGES)
...@@ -544,7 +544,7 @@ class TemplateForm(forms.ModelForm): ...@@ -544,7 +544,7 @@ class TemplateForm(forms.ModelForm):
else: else:
self.allowed_fields = ( self.allowed_fields = (
'name', 'access_method', 'description', 'system', 'tags', 'name', 'access_method', 'description', 'system', 'tags',
'arch', 'lease', 'has_agent') 'arch', 'lease', 'has_agent', 'cloud_init', 'ci_user_data', 'ci_meta_data')
if (self.user.has_perm('vm.change_template_resources') or if (self.user.has_perm('vm.change_template_resources') or
not self.instance.pk): not self.instance.pk):
self.allowed_fields += tuple(set(self.fields.keys()) - self.allowed_fields += tuple(set(self.fields.keys()) -
...@@ -1423,9 +1423,8 @@ class UserEditForm(forms.ModelForm): ...@@ -1423,9 +1423,8 @@ class UserEditForm(forms.ModelForm):
helper.add_input(Submit("submit", _("Save"))) helper.add_input(Submit("submit", _("Save")))
return helper return helper
class AclUserOrGroupAddForm(forms.Form): class AclUserOrGroupAddForm(forms.Form):
name = forms.CharField( name = autocomplete.Select2ListChoiceField(
widget=autocomplete.ListSelect2( widget=autocomplete.ListSelect2(
url='autocomplete.acl.user-group', url='autocomplete.acl.user-group',
attrs={'class': 'form-control', attrs={'class': 'form-control',
...@@ -1434,7 +1433,7 @@ class AclUserOrGroupAddForm(forms.Form): ...@@ -1434,7 +1433,7 @@ class AclUserOrGroupAddForm(forms.Form):
class TransferOwnershipForm(forms.Form): class TransferOwnershipForm(forms.Form):
name = forms.CharField( name = autocomplete.Select2ListChoiceField(
widget=autocomplete.ListSelect2( widget=autocomplete.ListSelect2(
url='autocomplete.acl.user', url='autocomplete.acl.user',
attrs={'class': 'form-control', attrs={'class': 'form-control',
...@@ -1444,7 +1443,7 @@ class TransferOwnershipForm(forms.Form): ...@@ -1444,7 +1443,7 @@ class TransferOwnershipForm(forms.Form):
class AddGroupMemberForm(forms.Form): class AddGroupMemberForm(forms.Form):
new_member = forms.CharField( new_member = autocomplete.Select2ListChoiceField(
widget=autocomplete.ListSelect2( widget=autocomplete.ListSelect2(
url='autocomplete.acl.user', url='autocomplete.acl.user',
attrs={'class': 'form-control', attrs={'class': 'form-control',
...@@ -1533,6 +1532,25 @@ class RawDataForm(forms.ModelForm): ...@@ -1533,6 +1532,25 @@ class RawDataForm(forms.ModelForm):
return helper return helper
class CIDataForm(forms.ModelForm):
ci_meta_data = forms.CharField(
widget=forms.Textarea(attrs={'rows': 5, 'style' : 'display:none;'}),
required=False)
ci_user_data = forms.CharField(
widget=forms.Textarea(attrs={'rows': 12, 'style' : 'display:none;'}),
required=False)
def clean(self):
data = self.cleaned_data
meta_data_validator(self.instance, data['ci_meta_data'])
user_data_validator(self.instance, data['ci_user_data'])
return super().clean()
class Meta:
model = Instance
fields = ('ci_meta_data', 'ci_user_data',)
class GroupPermissionForm(forms.ModelForm): class GroupPermissionForm(forms.ModelForm):
permissions = forms.ModelMultipleChoiceField( permissions = forms.ModelMultipleChoiceField(
queryset=None, queryset=None,
......
This source diff could not be displayed because it is too large. You can view the blob instead.
ace.define("ace/ext/beautify",["require","exports","module","ace/token_iterator"],function(e,t,n){"use strict";function i(e,t){return e.type.lastIndexOf(t+".xml")>-1}var r=e("../token_iterator").TokenIterator;t.singletonTags=["area","base","br","col","command","embed","hr","html","img","input","keygen","link","meta","param","source","track","wbr"],t.blockTags=["article","aside","blockquote","body","div","dl","fieldset","footer","form","head","header","html","nav","ol","p","script","section","style","table","tbody","tfoot","thead","ul"],t.formatOptions={lineBreaksAfterCommasInCurlyBlock:!0},t.beautify=function(e){var n=new r(e,0,0),s=n.getCurrentToken(),o=e.getTabString(),u=t.singletonTags,a=t.blockTags,f=t.formatOptions||{},l,c=!1,h=!1,p=!1,d="",v="",m="",g=0,y=0,b=0,w=0,E=0,S=0,x=0,T,N=0,C=0,k=[],L=!1,A,O=!1,M=!1,_=!1,D=!1,P={0:0},H=[],B=!1,j=function(){l&&l.value&&l.type!=="string.regexp"&&(l.value=l.value.replace(/^\s*/,""))},F=function(){var e=d.length-1;for(;;){if(e==0)break;if(d[e]!==" ")break;e-=1}d=d.slice(0,e+1)},I=function(){d=d.trimRight(),c=!1};while(s!==null){N=n.getCurrentTokenRow(),k=n.$rowTokens,l=n.stepForward();if(typeof s!="undefined"){v=s.value,E=0,_=m==="style"||e.$modeId==="ace/mode/css",i(s,"tag-open")?(M=!0,l&&(D=a.indexOf(l.value)!==-1),v==="</"&&(D&&!c&&C<1&&C++,_&&(C=1),E=1,D=!1)):i(s,"tag-close")?M=!1:i(s,"comment.start")?D=!0:i(s,"comment.end")&&(D=!1),!M&&!C&&s.type==="paren.rparen"&&s.value.substr(0,1)==="}"&&C++,N!==T&&(C=N,T&&(C-=T));if(C){I();for(;C>0;C--)d+="\n";c=!0,!i(s,"comment")&&!s.type.match(/^(comment|string)$/)&&(v=v.trimLeft())}if(v){s.type==="keyword"&&v.match(/^(if|else|elseif|for|foreach|while|switch)$/)?(H[g]=v,j(),p=!0,v.match(/^(else|elseif)$/)&&d.match(/\}[\s]*$/)&&(I(),h=!0)):s.type==="paren.lparen"?(j(),v.substr(-1)==="{"&&(p=!0,O=!1,M||(C=1)),v.substr(0,1)==="{"&&(h=!0,d.substr(-1)!=="["&&d.trimRight().substr(-1)==="["?(I(),h=!1):d.trimRight().substr(-1)===")"?I():F())):s.type==="paren.rparen"?(E=1,v.substr(0,1)==="}"&&(H[g-1]==="case"&&E++,d.trimRight().substr(-1)==="{"?I():(h=!0,_&&(C+=2))),v.substr(0,1)==="]"&&d.substr(-1)!=="}"&&d.trimRight().substr(-1)==="}"&&(h=!1,w++,I()),v.substr(0,1)===")"&&d.substr(-1)!=="("&&d.trimRight().substr(-1)==="("&&(h=!1,w++,I()),F()):s.type!=="keyword.operator"&&s.type!=="keyword"||!v.match(/^(=|==|===|!=|!==|&&|\|\||and|or|xor|\+=|.=|>|>=|<|<=|=>)$/)?s.type==="punctuation.operator"&&v===";"?(I(),j(),p=!0,_&&C++):s.type==="punctuation.operator"&&v.match(/^(:|,)$/)?(I(),j(),v.match(/^(,)$/)&&x>0&&S===0&&f.lineBreaksAfterCommasInCurlyBlock?C++:(p=!0,c=!1)):s.type==="support.php_tag"&&v==="?>"&&!c?(I(),h=!0):i(s,"attribute-name")&&d.substr(-1).match(/^\s$/)?h=!0:i(s,"attribute-equals")?(F(),j()):i(s,"tag-close")?(F(),v==="/>"&&(h=!0)):s.type==="keyword"&&v.match(/^(case|default)$/)&&B&&(E=1):(I(),j(),h=!0,p=!0);if(c&&(!s.type.match(/^(comment)$/)||!!v.substr(0,1).match(/^[/#]$/))&&(!s.type.match(/^(string)$/)||!!v.substr(0,1).match(/^['"@]$/))){w=b;if(g>y){w++;for(A=g;A>y;A--)P[A]=w}else g<y&&(w=P[g]);y=g,b=w,E&&(w-=E),O&&!S&&(w++,O=!1);for(A=0;A<w;A++)d+=o}s.type==="keyword"&&v.match(/^(case|default)$/)?B===!1&&(H[g]=v,g++,B=!0):s.type==="keyword"&&v.match(/^(break)$/)&&H[g-1]&&H[g-1].match(/^(case|default)$/)&&(g--,B=!1),s.type==="paren.lparen"&&(S+=(v.match(/\(/g)||[]).length,x+=(v.match(/\{/g)||[]).length,g+=v.length),s.type==="keyword"&&v.match(/^(if|else|elseif|for|while)$/)?(O=!0,S=0):!S&&v.trim()&&s.type!=="comment"&&(O=!1);if(s.type==="paren.rparen"){S-=(v.match(/\)/g)||[]).length,x-=(v.match(/\}/g)||[]).length;for(A=0;A<v.length;A++)g--,v.substr(A,1)==="}"&&H[g]==="case"&&g--}s.type=="text"&&(v=v.replace(/\s+$/," ")),h&&!c&&(F(),d.substr(-1)!=="\n"&&(d+=" ")),d+=v,p&&(d+=" "),c=!1,h=!1,p=!1;if(i(s,"tag-close")&&(D||a.indexOf(m)!==-1)||i(s,"doctype")&&v===">")D&&l&&l.value==="</"?C=-1:C=1;l&&u.indexOf(l.value)===-1&&(i(s,"tag-open")&&v==="</"?g--:i(s,"tag-open")&&v==="<"?g++:i(s,"tag-close")&&v==="/>"&&g--),i(s,"tag-name")&&(m=v),T=N}}s=l}d=d.trim(),e.doc.setValue(d)},t.commands=[{name:"beautify",description:"Format selection (Beautify)",exec:function(e){t.beautify(e.session)},bindKey:"Ctrl-Shift-B"}]}); (function() {
ace.require(["ace/ext/beautify"], function(m) {
if (typeof module == "object" && typeof exports == "object" && module) {
module.exports = m;
}
});
})();
\ No newline at end of file
ace.define("ace/ext/textarea",["require","exports","module","ace/lib/event","ace/lib/useragent","ace/lib/net","ace/ace"],function(e,t,n){"use strict";function a(e,t){for(var n in t)e.style[n]=t[n]}function f(e,t){if(e.type!="textarea")throw new Error("Textarea required!");var n=e.parentNode,i=document.createElement("div"),s=function(){var t="position:relative;";["margin-top","margin-left","margin-right","margin-bottom"].forEach(function(n){t+=n+":"+u(e,i,n)+";"});var n=u(e,i,"width")||e.clientWidth+"px",r=u(e,i,"height")||e.clientHeight+"px";t+="height:"+r+";width:"+n+";",t+="display:inline-block;",i.setAttribute("style",t)};r.addListener(window,"resize",s),s(),n.insertBefore(i,e.nextSibling);while(n!==document){if(n.tagName.toUpperCase()==="FORM"){var o=n.onsubmit;n.onsubmit=function(n){e.value=t(),o&&o.call(this,n)};break}n=n.parentNode}return i}function l(t,n,r){s.loadScript(t,function(){e([n],r)})}function c(e,t,n,r,i){function u(e){return e==="true"||e==1}var s=e.getSession(),o=e.renderer;return e.setDisplaySettings=function(t){t==null&&(t=n.style.display=="none"),t?(n.style.display="block",n.hideButton.focus(),e.on("focus",function r(){e.removeListener("focus",r),n.style.display="none"})):e.focus()},e.$setOption=e.setOption,e.$getOption=e.getOption,e.setOption=function(t,n){switch(t){case"mode":e.$setOption("mode","ace/mode/"+n);break;case"theme":e.$setOption("theme","ace/theme/"+n);break;case"keybindings":switch(n){case"vim":e.setKeyboardHandler("ace/keyboard/vim");break;case"emacs":e.setKeyboardHandler("ace/keyboard/emacs");break;default:e.setKeyboardHandler(null)}break;case"wrap":case"fontSize":e.$setOption(t,n);break;default:e.$setOption(t,u(n))}},e.getOption=function(t){switch(t){case"mode":return e.$getOption("mode").substr("ace/mode/".length);case"theme":return e.$getOption("theme").substr("ace/theme/".length);case"keybindings":var n=e.getKeyboardHandler();switch(n&&n.$id){case"ace/keyboard/vim":return"vim";case"ace/keyboard/emacs":return"emacs";default:return"ace"}break;default:return e.$getOption(t)}},e.setOptions(i),e}function h(e,n,i){function f(e,t,n,r){if(!n){e.push("<input type='checkbox' title='",t,"' ",r+""=="true"?"checked='true'":"","'></input>");return}e.push("<select title='"+t+"'>");for(var i in n)e.push("<option value='"+i+"' "),r==i&&e.push(" selected "),e.push(">",n[i],"</option>");e.push("</select>")}var s=null,o={mode:"Mode:",wrap:"Soft Wrap:",theme:"Theme:",fontSize:"Font Size:",showGutter:"Display Gutter:",keybindings:"Keyboard",showPrintMargin:"Show Print Margin:",useSoftTabs:"Use Soft Tabs:",showInvisibles:"Show Invisibles"},u={mode:{text:"Plain",javascript:"JavaScript",xml:"XML",html:"HTML",css:"CSS",scss:"SCSS",python:"Python",php:"PHP",java:"Java",ruby:"Ruby",c_cpp:"C/C++",coffee:"CoffeeScript",json:"json",perl:"Perl",clojure:"Clojure",ocaml:"OCaml",csharp:"C#",haxe:"haXe",svg:"SVG",textile:"Textile",groovy:"Groovy",liquid:"Liquid",Scala:"Scala"},theme:{clouds:"Clouds",clouds_midnight:"Clouds Midnight",cobalt:"Cobalt",crimson_editor:"Crimson Editor",dawn:"Dawn",gob:"Green on Black",eclipse:"Eclipse",idle_fingers:"Idle Fingers",kr_theme:"Kr Theme",merbivore:"Merbivore",merbivore_soft:"Merbivore Soft",mono_industrial:"Mono Industrial",monokai:"Monokai",pastel_on_dark:"Pastel On Dark",solarized_dark:"Solarized Dark",solarized_light:"Solarized Light",textmate:"Textmate",twilight:"Twilight",vibrant_ink:"Vibrant Ink"},showGutter:s,fontSize:{"10px":"10px","11px":"11px","12px":"12px","14px":"14px","16px":"16px"},wrap:{off:"Off",40:"40",80:"80",free:"Free"},keybindings:{ace:"ace",vim:"vim",emacs:"emacs"},showPrintMargin:s,useSoftTabs:s,showInvisibles:s},a=[];a.push("<table><tr><th>Setting</th><th>Value</th></tr>");for(var l in t.defaultOptions)a.push("<tr><td>",o[l],"</td>"),a.push("<td>"),f(a,l,u[l],i.getOption(l)),a.push("</td></tr>");a.push("</table>"),e.innerHTML=a.join("");var c=function(e){var t=e.currentTarget;i.setOption(t.title,t.value)},h=function(e){var t=e.currentTarget;i.setOption(t.title,t.checked)},p=e.getElementsByTagName("select");for(var d=0;d<p.length;d++)p[d].onchange=c;var v=e.getElementsByTagName("input");for(var d=0;d<v.length;d++)v[d].onclick=h;var m=document.createElement("input");m.type="button",m.value="Hide",r.addListener(m,"click",function(){i.setDisplaySettings(!1)}),e.appendChild(m),e.hideButton=m}var r=e("../lib/event"),i=e("../lib/useragent"),s=e("../lib/net"),o=e("../ace");n.exports=t=o;var u=function(e,t,n){var r=e.style[n];r||(window.getComputedStyle?r=window.getComputedStyle(e,"").getPropertyValue(n):r=e.currentStyle[n]);if(!r||r=="auto"||r=="intrinsic")r=t.style[n];return r};t.transformTextarea=function(e,n){var s=e.autofocus||document.activeElement==e,u,l=f(e,function(){return u.getValue()});e.style.display="none",l.style.background="white";var p=document.createElement("div");a(p,{top:"0px",left:"0px",right:"0px",bottom:"0px",border:"1px solid gray",position:"absolute"}),l.appendChild(p);var d=document.createElement("div");a(d,{position:"absolute",right:"0px",bottom:"0px",cursor:"nw-resize",border:"solid 9px",borderColor:"lightblue gray gray #ceade6",zIndex:101});var v=document.createElement("div"),m={top:"0px",left:"20%",right:"0px",bottom:"0px",position:"absolute",padding:"5px",zIndex:100,color:"white",display:"none",overflow:"auto",fontSize:"14px",boxShadow:"-5px 2px 3px gray"};i.isOldIE?m.backgroundColor="#333":m.backgroundColor="rgba(0, 0, 0, 0.6)",a(v,m),l.appendChild(v),n=n||t.defaultOptions;var g=o.edit(p);u=g.getSession(),u.setValue(e.value||e.innerHTML),s&&g.focus(),l.appendChild(d),c(g,p,v,o,n),h(v,d,g);var y="";return r.addListener(d,"mousemove",function(e){var t=this.getBoundingClientRect(),n=e.clientX-t.left,r=e.clientY-t.top;n+r<(t.width+t.height)/2?(this.style.cursor="pointer",y="toggle"):(y="resize",this.style.cursor="nw-resize")}),r.addListener(d,"mousedown",function(e){e.preventDefault();if(y=="toggle"){g.setDisplaySettings();return}l.style.zIndex=1e5;var t=l.getBoundingClientRect(),n=t.width+t.left-e.clientX,i=t.height+t.top-e.clientY;r.capture(d,function(e){l.style.width=e.clientX-t.left+n+"px",l.style.height=e.clientY-t.top+i+"px",g.resize()},function(){})}),g},t.defaultOptions={mode:"javascript",theme:"textmate",wrap:"off",fontSize:"12px",showGutter:"false",keybindings:"ace",showPrintMargin:"false",useSoftTabs:"true",showInvisibles:"false"}}); (function() {
ace.require(["ace/ext/textarea"], function(m) {
if (typeof module == "object" && typeof exports == "object" && module) {
module.exports = m;
}
});
})();
\ No newline at end of file
ace.define("ace/ext/themelist",["require","exports","module"],function(e,t,n){"use strict";var r=[["Chrome"],["Clouds"],["Crimson Editor"],["Dawn"],["Dreamweaver"],["Eclipse"],["GitHub"],["IPlastic"],["Solarized Light"],["TextMate"],["Tomorrow"],["XCode"],["Kuroir"],["KatzenMilch"],["SQL Server","sqlserver","light"],["Ambiance","ambiance","dark"],["Chaos","chaos","dark"],["Clouds Midnight","clouds_midnight","dark"],["Dracula","","dark"],["Cobalt","cobalt","dark"],["Gruvbox","gruvbox","dark"],["Green on Black","gob","dark"],["idle Fingers","idle_fingers","dark"],["krTheme","kr_theme","dark"],["Merbivore","merbivore","dark"],["Merbivore Soft","merbivore_soft","dark"],["Mono Industrial","mono_industrial","dark"],["Monokai","monokai","dark"],["Nord Dark","nord_dark","dark"],["One Dark","one_dark","dark"],["Pastel on dark","pastel_on_dark","dark"],["Solarized Dark","solarized_dark","dark"],["Terminal","terminal","dark"],["Tomorrow Night","tomorrow_night","dark"],["Tomorrow Night Blue","tomorrow_night_blue","dark"],["Tomorrow Night Bright","tomorrow_night_bright","dark"],["Tomorrow Night 80s","tomorrow_night_eighties","dark"],["Twilight","twilight","dark"],["Vibrant Ink","vibrant_ink","dark"]];t.themesByName={},t.themes=r.map(function(e){var n=e[1]||e[0].replace(/ /g,"_").toLowerCase(),r={caption:e[0],theme:"ace/theme/"+n,isDark:e[2]=="dark",name:n};return t.themesByName[n]=r,r})}); (function() {
ace.require(["ace/ext/themelist"], function(m) {
if (typeof module == "object" && typeof exports == "object" && module) {
module.exports = m;
}
});
})();
\ No newline at end of file
ace.define("ace/ext/whitespace",["require","exports","module","ace/lib/lang"],function(e,t,n){"use strict";var r=e("../lib/lang");t.$detectIndentation=function(e,t){function c(e){var t=0;for(var r=e;r<n.length;r+=e)t+=n[r]||0;return t}var n=[],r=[],i=0,s=0,o=Math.min(e.length,1e3);for(var u=0;u<o;u++){var a=e[u];if(!/^\s*[^*+\-\s]/.test(a))continue;if(a[0]==" ")i++,s=-Number.MAX_VALUE;else{var f=a.match(/^ */)[0].length;if(f&&a[f]!=" "){var l=f-s;l>0&&!(s%l)&&!(f%l)&&(r[l]=(r[l]||0)+1),n[f]=(n[f]||0)+1}s=f}while(u<o&&a[a.length-1]=="\\")a=e[u++]}var h=r.reduce(function(e,t){return e+t},0),p={score:0,length:0},d=0;for(var u=1;u<12;u++){var v=c(u);u==1?(d=v,v=n[1]?.9:.8,n.length||(v=0)):v/=d,r[u]&&(v+=r[u]/h),v>p.score&&(p={score:v,length:u})}if(p.score&&p.score>1.4)var m=p.length;if(i>d+1){if(m==1||d<i/4||p.score<1.8)m=undefined;return{ch:" ",length:m}}if(d>i+1)return{ch:" ",length:m}},t.detectIndentation=function(e){var n=e.getLines(0,1e3),r=t.$detectIndentation(n)||{};return r.ch&&e.setUseSoftTabs(r.ch==" "),r.length&&e.setTabSize(r.length),r},t.trimTrailingSpace=function(e,t){var n=e.getDocument(),r=n.getAllLines(),i=t&&t.trimEmpty?-1:0,s=[],o=-1;t&&t.keepCursorPosition&&(e.selection.rangeCount?e.selection.rangeList.ranges.forEach(function(e,t,n){var r=n[t+1];if(r&&r.cursor.row==e.cursor.row)return;s.push(e.cursor)}):s.push(e.selection.getCursor()),o=0);var u=s[o]&&s[o].row;for(var a=0,f=r.length;a<f;a++){var l=r[a],c=l.search(/\s+$/);a==u&&(c<s[o].column&&c>i&&(c=s[o].column),o++,u=s[o]?s[o].row:-1),c>i&&n.removeInLine(a,c,l.length)}},t.convertIndentation=function(e,t,n){var i=e.getTabString()[0],s=e.getTabSize();n||(n=s),t||(t=i);var o=t==" "?t:r.stringRepeat(t,n),u=e.doc,a=u.getAllLines(),f={},l={};for(var c=0,h=a.length;c<h;c++){var p=a[c],d=p.match(/^\s*/)[0];if(d){var v=e.$getStringScreenWidth(d)[0],m=Math.floor(v/s),g=v%s,y=f[m]||(f[m]=r.stringRepeat(o,m));y+=l[g]||(l[g]=r.stringRepeat(" ",g)),y!=d&&(u.removeInLine(c,0,d.length),u.insertInLine({row:c,column:0},y))}}e.setTabSize(n),e.setUseSoftTabs(t==" ")},t.$parseStringArg=function(e){var t={};/t/.test(e)?t.ch=" ":/s/.test(e)&&(t.ch=" ");var n=e.match(/\d+/);return n&&(t.length=parseInt(n[0],10)),t},t.$parseArg=function(e){return e?typeof e=="string"?t.$parseStringArg(e):typeof e.text=="string"?t.$parseStringArg(e.text):e:{}},t.commands=[{name:"detectIndentation",description:"Detect indentation from content",exec:function(e){t.detectIndentation(e.session)}},{name:"trimTrailingSpace",description:"Trim trailing whitespace",exec:function(e,n){t.trimTrailingSpace(e.session,n)}},{name:"convertIndentation",description:"Convert indentation to ...",exec:function(e,n){var r=t.$parseArg(n);t.convertIndentation(e.session,r.ch,r.length)}},{name:"setIndentation",description:"Set indentation",exec:function(e,n){var r=t.$parseArg(n);r.length&&e.session.setTabSize(r.length),r.ch&&e.session.setUseSoftTabs(r.ch==" ")}}]}); (function() {
ace.require(["ace/ext/whitespace"], function(m) {
if (typeof module == "object" && typeof exports == "object" && module) {
module.exports = m;
}
});
})();
\ No newline at end of file
ace.define("ace/keyboard/sublime",["require","exports","module","ace/keyboard/hash_handler"],function(e,t,n){"use strict";function i(e,t,n){function f(e){return e?/\s/.test(e)?"s":e=="_"?"_":e.toUpperCase()==e&&e.toLowerCase()!=e?"W":e.toUpperCase()!=e&&e.toLowerCase()==e?"w":"o":"-"}var r=e.selection,i=r.lead.row,s=r.lead.column,o=e.session.getLine(i);if(!o[s+t]){var u=(n?"selectWord":"moveCursorShortWord")+(t==1?"Right":"Left");return e.selection[u]()}t==-1&&s--;while(o[s]){var a=f(o[s])+f(o[s+t]);s+=t;if(t==1){if(a=="WW"&&f(o[s+1])=="w")break}else{if(a=="wW"){if(f(o[s-1])=="W"){s-=1;break}continue}if(a=="Ww")break}if(/w[s_oW]|_[sWo]|o[s_wW]|s[W]|W[so]/.test(a))break}t==-1&&s++,n?e.selection.moveCursorTo(i,s):e.selection.moveTo(i,s)}var r=e("../keyboard/hash_handler").HashHandler;t.handler=new r,t.handler.addCommands([{name:"find_all_under",exec:function(e){e.selection.isEmpty()&&e.selection.selectWord(),e.findAll()},readOnly:!0},{name:"find_under",exec:function(e){e.selection.isEmpty()&&e.selection.selectWord(),e.findNext()},readOnly:!0},{name:"find_under_prev",exec:function(e){e.selection.isEmpty()&&e.selection.selectWord(),e.findPrevious()},readOnly:!0},{name:"find_under_expand",exec:function(e){e.selectMore(1,!1,!0)},scrollIntoView:"animate",readOnly:!0},{name:"find_under_expand_skip",exec:function(e){e.selectMore(1,!0,!0)},scrollIntoView:"animate",readOnly:!0},{name:"delete_to_hard_bol",exec:function(e){var t=e.selection.getCursor();e.session.remove({start:{row:t.row,column:0},end:t})},multiSelectAction:"forEach",scrollIntoView:"cursor"},{name:"delete_to_hard_eol",exec:function(e){var t=e.selection.getCursor();e.session.remove({start:t,end:{row:t.row,column:Infinity}})},multiSelectAction:"forEach",scrollIntoView:"cursor"},{name:"moveToWordStartLeft",exec:function(e){e.selection.moveCursorLongWordLeft(),e.clearSelection()},multiSelectAction:"forEach",scrollIntoView:"cursor"},{name:"moveToWordEndRight",exec:function(e){e.selection.moveCursorLongWordRight(),e.clearSelection()},multiSelectAction:"forEach",scrollIntoView:"cursor"},{name:"selectToWordStartLeft",exec:function(e){var t=e.selection;t.$moveSelection(t.moveCursorLongWordLeft)},multiSelectAction:"forEach",scrollIntoView:"cursor"},{name:"selectToWordEndRight",exec:function(e){var t=e.selection;t.$moveSelection(t.moveCursorLongWordRight)},multiSelectAction:"forEach",scrollIntoView:"cursor"},{name:"selectSubWordRight",exec:function(e){i(e,1,!0)},multiSelectAction:"forEach",scrollIntoView:"cursor",readOnly:!0},{name:"selectSubWordLeft",exec:function(e){i(e,-1,!0)},multiSelectAction:"forEach",scrollIntoView:"cursor",readOnly:!0},{name:"moveSubWordRight",exec:function(e){i(e,1)},multiSelectAction:"forEach",scrollIntoView:"cursor",readOnly:!0},{name:"moveSubWordLeft",exec:function(e){i(e,-1)},multiSelectAction:"forEach",scrollIntoView:"cursor",readOnly:!0}]),[{bindKey:{mac:"cmd-k cmd-backspace|cmd-backspace",win:"ctrl-shift-backspace|ctrl-k ctrl-backspace"},name:"removetolinestarthard"},{bindKey:{mac:"cmd-k cmd-k|cmd-delete|ctrl-k",win:"ctrl-shift-delete|ctrl-k ctrl-k"},name:"removetolineendhard"},{bindKey:{mac:"cmd-shift-d",win:"ctrl-shift-d"},name:"duplicateSelection"},{bindKey:{mac:"cmd-l",win:"ctrl-l"},name:"expandtoline"},{bindKey:{mac:"cmd-shift-a",win:"ctrl-shift-a"},name:"expandSelection",args:{to:"tag"}},{bindKey:{mac:"cmd-shift-j",win:"ctrl-shift-j"},name:"expandSelection",args:{to:"indentation"}},{bindKey:{mac:"ctrl-shift-m",win:"ctrl-shift-m"},name:"expandSelection",args:{to:"brackets"}},{bindKey:{mac:"cmd-shift-space",win:"ctrl-shift-space"},name:"expandSelection",args:{to:"scope"}},{bindKey:{mac:"ctrl-cmd-g",win:"alt-f3"},name:"find_all_under"},{bindKey:{mac:"alt-cmd-g",win:"ctrl-f3"},name:"find_under"},{bindKey:{mac:"shift-alt-cmd-g",win:"ctrl-shift-f3"},name:"find_under_prev"},{bindKey:{mac:"cmd-g",win:"f3"},name:"findnext"},{bindKey:{mac:"shift-cmd-g",win:"shift-f3"},name:"findprevious"},{bindKey:{mac:"cmd-d",win:"ctrl-d"},name:"find_under_expand"},{bindKey:{mac:"cmd-k cmd-d",win:"ctrl-k ctrl-d"},name:"find_under_expand_skip"},{bindKey:{mac:"cmd-alt-[",win:"ctrl-shift-["},name:"toggleFoldWidget"},{bindKey:{mac:"cmd-alt-]",win:"ctrl-shift-]"},name:"unfold"},{bindKey:{mac:"cmd-k cmd-0|cmd-k cmd-j",win:"ctrl-k ctrl-0|ctrl-k ctrl-j"},name:"unfoldall"},{bindKey:{mac:"cmd-k cmd-1",win:"ctrl-k ctrl-1"},name:"foldOther",args:{level:1}},{bindKey:{win:"ctrl-left",mac:"alt-left"},name:"moveToWordStartLeft"},{bindKey:{win:"ctrl-right",mac:"alt-right"},name:"moveToWordEndRight"},{bindKey:{win:"ctrl-shift-left",mac:"alt-shift-left"},name:"selectToWordStartLeft"},{bindKey:{win:"ctrl-shift-right",mac:"alt-shift-right"},name:"selectToWordEndRight"},{bindKey:{mac:"ctrl-alt-shift-right|ctrl-shift-right",win:"alt-shift-right"},name:"selectSubWordRight"},{bindKey:{mac:"ctrl-alt-shift-left|ctrl-shift-left",win:"alt-shift-left"},name:"selectSubWordLeft"},{bindKey:{mac:"ctrl-alt-right|ctrl-right",win:"alt-right"},name:"moveSubWordRight"},{bindKey:{mac:"ctrl-alt-left|ctrl-left",win:"alt-left"},name:"moveSubWordLeft"},{bindKey:{mac:"ctrl-m",win:"ctrl-m"},name:"jumptomatching",args:{to:"brackets"}},{bindKey:{mac:"ctrl-f6",win:"ctrl-f6"},name:"goToNextError"},{bindKey:{mac:"ctrl-shift-f6",win:"ctrl-shift-f6"},name:"goToPreviousError"},{bindKey:{mac:"ctrl-o"},name:"splitline"},{bindKey:{mac:"ctrl-shift-w",win:"alt-shift-w"},name:"surrowndWithTag"},{bindKey:{mac:"cmd-alt-.",win:"alt-."},name:"close_tag"},{bindKey:{mac:"cmd-j",win:"ctrl-j"},name:"joinlines"},{bindKey:{mac:"ctrl--",win:"alt--"},name:"jumpBack"},{bindKey:{mac:"ctrl-shift--",win:"alt-shift--"},name:"jumpForward"},{bindKey:{mac:"cmd-k cmd-l",win:"ctrl-k ctrl-l"},name:"tolowercase"},{bindKey:{mac:"cmd-k cmd-u",win:"ctrl-k ctrl-u"},name:"touppercase"},{bindKey:{mac:"cmd-shift-v",win:"ctrl-shift-v"},name:"paste_and_indent"},{bindKey:{mac:"cmd-k cmd-v|cmd-alt-v",win:"ctrl-k ctrl-v"},name:"paste_from_history"},{bindKey:{mac:"cmd-shift-enter",win:"ctrl-shift-enter"},name:"addLineBefore"},{bindKey:{mac:"cmd-enter",win:"ctrl-enter"},name:"addLineAfter"},{bindKey:{mac:"ctrl-shift-k",win:"ctrl-shift-k"},name:"removeline"},{bindKey:{mac:"ctrl-alt-up",win:"ctrl-up"},name:"scrollup"},{bindKey:{mac:"ctrl-alt-down",win:"ctrl-down"},name:"scrolldown"},{bindKey:{mac:"cmd-a",win:"ctrl-a"},name:"selectall"},{bindKey:{linux:"alt-shift-down",mac:"ctrl-shift-down",win:"ctrl-alt-down"},name:"addCursorBelow"},{bindKey:{linux:"alt-shift-up",mac:"ctrl-shift-up",win:"ctrl-alt-up"},name:"addCursorAbove"},{bindKey:{mac:"cmd-k cmd-c|ctrl-l",win:"ctrl-k ctrl-c"},name:"centerselection"},{bindKey:{mac:"f5",win:"f9"},name:"sortlines"},{bindKey:{mac:"ctrl-f5",win:"ctrl-f9"},name:"sortlines",args:{caseSensitive:!0}},{bindKey:{mac:"cmd-shift-l",win:"ctrl-shift-l"},name:"splitSelectionIntoLines"},{bindKey:{mac:"ctrl-cmd-down",win:"ctrl-shift-down"},name:"movelinesdown"},{bindKey:{mac:"ctrl-cmd-up",win:"ctrl-shift-up"},name:"movelinesup"},{bindKey:{mac:"alt-down",win:"alt-down"},name:"modifyNumberDown"},{bindKey:{mac:"alt-up",win:"alt-up"},name:"modifyNumberUp"},{bindKey:{mac:"cmd-/",win:"ctrl-/"},name:"togglecomment"},{bindKey:{mac:"cmd-alt-/",win:"ctrl-shift-/"},name:"toggleBlockComment"},{bindKey:{linux:"ctrl-alt-q",mac:"ctrl-q",win:"ctrl-q"},name:"togglerecording"},{bindKey:{linux:"ctrl-alt-shift-q",mac:"ctrl-shift-q",win:"ctrl-shift-q"},name:"replaymacro"},{bindKey:{mac:"ctrl-t",win:"ctrl-t"},name:"transpose"}].forEach(function(e){var n=t.handler.commands[e.name];n&&(n.bindKey=e.bindKey),t.handler.bindKey(e.bindKey,n||e.name)})}); (function() {
ace.require(["ace/keyboard/sublime"], function(m) {
if (typeof module == "object" && typeof exports == "object" && module) {
module.exports = m;
}
});
})();
\ No newline at end of file
This source diff could not be displayed because it is too large. You can view the blob instead.
ace.define("ace/keyboard/vscode",["require","exports","module","ace/keyboard/hash_handler","ace/config"],function(e,t,n){"use strict";var r=e("../keyboard/hash_handler").HashHandler,i=e("../config");t.handler=new r,t.handler.$id="ace/keyboard/vscode",t.handler.addCommands([{name:"toggleWordWrap",exec:function(e){var t=e.session.getUseWrapMode();e.session.setUseWrapMode(!t)},readOnly:!0},{name:"navigateToLastEditLocation",exec:function(e){var t=e.session.getUndoManager().$lastDelta,n=t.action=="remove"?t.start:t.end;e.moveCursorTo(n.row,n.column),e.clearSelection()}},{name:"replaceAll",exec:function(e){e.searchBox?e.searchBox.active===!0&&e.searchBox.replaceOption.checked===!0&&e.searchBox.replaceAll():i.loadModule("ace/ext/searchbox",function(t){t.Search(e,!0)})}},{name:"replaceOne",exec:function(e){e.searchBox?e.searchBox.active===!0&&e.searchBox.replaceOption.checked===!0&&e.searchBox.replace():i.loadModule("ace/ext/searchbox",function(t){t.Search(e,!0)})}},{name:"selectAllMatches",exec:function(e){e.searchBox?e.searchBox.active===!0&&e.searchBox.findAll():i.loadModule("ace/ext/searchbox",function(t){t.Search(e,!1)})}},{name:"toggleFindCaseSensitive",exec:function(e){i.loadModule("ace/ext/searchbox",function(t){t.Search(e,!1);var n=e.searchBox;n.caseSensitiveOption.checked=!n.caseSensitiveOption.checked,n.$syncOptions()})}},{name:"toggleFindInSelection",exec:function(e){i.loadModule("ace/ext/searchbox",function(t){t.Search(e,!1);var n=e.searchBox;n.searchOption.checked=!n.searchRange,n.setSearchRange(n.searchOption.checked&&n.editor.getSelectionRange()),n.$syncOptions()})}},{name:"toggleFindRegex",exec:function(e){i.loadModule("ace/ext/searchbox",function(t){t.Search(e,!1);var n=e.searchBox;n.regExpOption.checked=!n.regExpOption.checked,n.$syncOptions()})}},{name:"toggleFindWholeWord",exec:function(e){i.loadModule("ace/ext/searchbox",function(t){t.Search(e,!1);var n=e.searchBox;n.wholeWordOption.checked=!n.wholeWordOption.checked,n.$syncOptions()})}},{name:"removeSecondaryCursors",exec:function(e){var t=e.selection.ranges;t&&t.length>1?e.selection.toSingleRange(t[t.length-1]):e.selection.clearSelection()}}]),[{bindKey:{mac:"Ctrl-G",win:"Ctrl-G"},name:"gotoline"},{bindKey:{mac:"Command-Shift-L|Command-F2",win:"Ctrl-Shift-L|Ctrl-F2"},name:"findAll"},{bindKey:{mac:"Shift-F8|Shift-Option-F8",win:"Shift-F8|Shift-Alt-F8"},name:"goToPreviousError"},{bindKey:{mac:"F8|Option-F8",win:"F8|Alt-F8"},name:"goToNextError"},{bindKey:{mac:"Command-Shift-P|F1",win:"Ctrl-Shift-P|F1"},name:"openCommandPallete"},{bindKey:{mac:"Command-K|Command-S",win:"Ctrl-K|Ctrl-S"},name:"showKeyboardShortcuts"},{bindKey:{mac:"Shift-Option-Up",win:"Alt-Shift-Up"},name:"copylinesup"},{bindKey:{mac:"Shift-Option-Down",win:"Alt-Shift-Down"},name:"copylinesdown"},{bindKey:{mac:"Command-Shift-K",win:"Ctrl-Shift-K"},name:"removeline"},{bindKey:{mac:"Command-Enter",win:"Ctrl-Enter"},name:"addLineAfter"},{bindKey:{mac:"Command-Shift-Enter",win:"Ctrl-Shift-Enter"},name:"addLineBefore"},{bindKey:{mac:"Command-Shift-\\",win:"Ctrl-Shift-\\"},name:"jumptomatching"},{bindKey:{mac:"Command-]",win:"Ctrl-]"},name:"blockindent"},{bindKey:{mac:"Command-[",win:"Ctrl-["},name:"blockoutdent"},{bindKey:{mac:"Ctrl-PageDown",win:"Alt-PageDown"},name:"pagedown"},{bindKey:{mac:"Ctrl-PageUp",win:"Alt-PageUp"},name:"pageup"},{bindKey:{mac:"Shift-Option-A",win:"Shift-Alt-A"},name:"toggleBlockComment"},{bindKey:{mac:"Option-Z",win:"Alt-Z"},name:"toggleWordWrap"},{bindKey:{mac:"Command-G",win:"F3|Ctrl-K Ctrl-D"},name:"findnext"},{bindKey:{mac:"Command-Shift-G",win:"Shift-F3"},name:"findprevious"},{bindKey:{mac:"Option-Enter",win:"Alt-Enter"},name:"selectAllMatches"},{bindKey:{mac:"Command-D",win:"Ctrl-D"},name:"selectMoreAfter"},{bindKey:{mac:"Command-K Command-D",win:"Ctrl-K Ctrl-D"},name:"selectOrFindNext"},{bindKey:{mac:"Shift-Option-I",win:"Shift-Alt-I"},name:"splitSelectionIntoLines"},{bindKey:{mac:"Command-K M",win:"Ctrl-K M"},name:"modeSelect"},{bindKey:{mac:"Command-Option-[",win:"Ctrl-Shift-["},name:"toggleFoldWidget"},{bindKey:{mac:"Command-Option-]",win:"Ctrl-Shift-]"},name:"toggleFoldWidget"},{bindKey:{mac:"Command-K Command-0",win:"Ctrl-K Ctrl-0"},name:"foldall"},{bindKey:{mac:"Command-K Command-J",win:"Ctrl-K Ctrl-J"},name:"unfoldall"},{bindKey:{mac:"Command-K Command-1",win:"Ctrl-K Ctrl-1"},name:"foldOther"},{bindKey:{mac:"Command-K Command-Q",win:"Ctrl-K Ctrl-Q"},name:"navigateToLastEditLocation"},{bindKey:{mac:"Command-K Command-R|Command-K Command-S",win:"Ctrl-K Ctrl-R|Ctrl-K Ctrl-S"},name:"showKeyboardShortcuts"},{bindKey:{mac:"Command-K Command-X",win:"Ctrl-K Ctrl-X"},name:"trimTrailingSpace"},{bindKey:{mac:"Shift-Down|Command-Shift-Down",win:"Shift-Down|Ctrl-Shift-Down"},name:"selectdown"},{bindKey:{mac:"Shift-Up|Command-Shift-Up",win:"Shift-Up|Ctrl-Shift-Up"},name:"selectup"},{bindKey:{mac:"Command-Alt-Enter",win:"Ctrl-Alt-Enter"},name:"replaceAll"},{bindKey:{mac:"Command-Shift-1",win:"Ctrl-Shift-1"},name:"replaceOne"},{bindKey:{mac:"Option-C",win:"Alt-C"},name:"toggleFindCaseSensitive"},{bindKey:{mac:"Option-L",win:"Alt-L"},name:"toggleFindInSelection"},{bindKey:{mac:"Option-R",win:"Alt-R"},name:"toggleFindRegex"},{bindKey:{mac:"Option-W",win:"Alt-W"},name:"toggleFindWholeWord"},{bindKey:{mac:"Command-L",win:"Ctrl-L"},name:"expandtoline"},{bindKey:{mac:"Shift-Esc",win:"Shift-Esc"},name:"removeSecondaryCursors"}].forEach(function(e){var n=t.handler.commands[e.name];n&&(n.bindKey=e.bindKey),t.handler.bindKey(e.bindKey,n||e.name)})}); (function() {
ace.require(["ace/keyboard/vscode"], function(m) {
if (typeof module == "object" && typeof exports == "object" && module) {
module.exports = m;
}
});
})();
\ No newline at end of file
ace.define("ace/mode/abc_highlight_rules",["require","exports","module","ace/lib/oop","ace/mode/text_highlight_rules"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("./text_highlight_rules").TextHighlightRules,s=function(){this.$rules={start:[{token:["zupfnoter.information.comment.line.percentage","information.keyword","in formation.keyword.embedded"],regex:"(%%%%)(hn\\.[a-z]*)(.*)",comment:"Instruction Comment"},{token:["information.comment.line.percentage","information.keyword.embedded"],regex:"(%%)(.*)",comment:"Instruction Comment"},{token:"comment.line.percentage",regex:"%.*",comment:"Comments"},{token:"barline.keyword.operator",regex:"[\\[:]*[|:][|\\]:]*(?:\\[?[0-9]+)?|\\[[0-9]+",comment:"Bar lines"},{token:["information.keyword.embedded","information.argument.string.unquoted"],regex:"(\\[[A-Za-z]:)([^\\]]*\\])",comment:"embedded Header lines"},{token:["information.keyword","information.argument.string.unquoted"],regex:"^([A-Za-z]:)([^%\\\\]*)",comment:"Header lines"},{token:["text","entity.name.function","string.unquoted","text"],regex:"(\\[)([A-Z]:)(.*?)(\\])",comment:"Inline fields"},{token:["accent.constant.language","pitch.constant.numeric","duration.constant.numeric"],regex:"([\\^=_]*)([A-Ga-gz][,']*)([0-9]*/*[><0-9]*)",comment:"Notes"},{token:"zupfnoter.jumptarget.string.quoted",regex:'[\\"!]\\^\\:.*?[\\"!]',comment:"Zupfnoter jumptarget"},{token:"zupfnoter.goto.string.quoted",regex:'[\\"!]\\^\\@.*?[\\"!]',comment:"Zupfnoter goto"},{token:"zupfnoter.annotation.string.quoted",regex:'[\\"!]\\^\\!.*?[\\"!]',comment:"Zupfnoter annoation"},{token:"zupfnoter.annotationref.string.quoted",regex:'[\\"!]\\^\\#.*?[\\"!]',comment:"Zupfnoter annotation reference"},{token:"chordname.string.quoted",regex:'[\\"!]\\^.*?[\\"!]',comment:"abc chord"},{token:"string.quoted",regex:'[\\"!].*?[\\"!]',comment:"abc annotation"}]},this.normalizeRules()};s.metaData={fileTypes:["abc"],name:"ABC",scopeName:"text.abcnotation"},r.inherits(s,i),t.ABCHighlightRules=s}),ace.define("ace/mode/folding/cstyle",["require","exports","module","ace/lib/oop","ace/range","ace/mode/folding/fold_mode"],function(e,t,n){"use strict";var r=e("../../lib/oop"),i=e("../../range").Range,s=e("./fold_mode").FoldMode,o=t.FoldMode=function(e){e&&(this.foldingStartMarker=new RegExp(this.foldingStartMarker.source.replace(/\|[^|]*?$/,"|"+e.start)),this.foldingStopMarker=new RegExp(this.foldingStopMarker.source.replace(/\|[^|]*?$/,"|"+e.end)))};r.inherits(o,s),function(){this.foldingStartMarker=/([\{\[\(])[^\}\]\)]*$|^\s*(\/\*)/,this.foldingStopMarker=/^[^\[\{\(]*([\}\]\)])|^[\s\*]*(\*\/)/,this.singleLineBlockCommentRe=/^\s*(\/\*).*\*\/\s*$/,this.tripleStarBlockCommentRe=/^\s*(\/\*\*\*).*\*\/\s*$/,this.startRegionRe=/^\s*(\/\*|\/\/)#?region\b/,this._getFoldWidgetBase=this.getFoldWidget,this.getFoldWidget=function(e,t,n){var r=e.getLine(n);if(this.singleLineBlockCommentRe.test(r)&&!this.startRegionRe.test(r)&&!this.tripleStarBlockCommentRe.test(r))return"";var i=this._getFoldWidgetBase(e,t,n);return!i&&this.startRegionRe.test(r)?"start":i},this.getFoldWidgetRange=function(e,t,n,r){var i=e.getLine(n);if(this.startRegionRe.test(i))return this.getCommentRegionBlock(e,i,n);var s=i.match(this.foldingStartMarker);if(s){var o=s.index;if(s[1])return this.openingBracketBlock(e,s[1],n,o);var u=e.getCommentFoldRange(n,o+s[0].length,1);return u&&!u.isMultiLine()&&(r?u=this.getSectionRange(e,n):t!="all"&&(u=null)),u}if(t==="markbegin")return;var s=i.match(this.foldingStopMarker);if(s){var o=s.index+s[0].length;return s[1]?this.closingBracketBlock(e,s[1],n,o):e.getCommentFoldRange(n,o,-1)}},this.getSectionRange=function(e,t){var n=e.getLine(t),r=n.search(/\S/),s=t,o=n.length;t+=1;var u=t,a=e.getLength();while(++t<a){n=e.getLine(t);var f=n.search(/\S/);if(f===-1)continue;if(r>f)break;var l=this.getFoldWidgetRange(e,"all",t);if(l){if(l.start.row<=s)break;if(l.isMultiLine())t=l.end.row;else if(r==f)break}u=t}return new i(s,o,u,e.getLine(u).length)},this.getCommentRegionBlock=function(e,t,n){var r=t.search(/\s*$/),s=e.getLength(),o=n,u=/^\s*(?:\/\*|\/\/|--)#?(end)?region\b/,a=1;while(++n<s){t=e.getLine(n);var f=u.exec(t);if(!f)continue;f[1]?a--:a++;if(!a)break}var l=n;if(l>o)return new i(o,r,l,t.length)}}.call(o.prototype)}),ace.define("ace/mode/abc",["require","exports","module","ace/lib/oop","ace/mode/text","ace/mode/abc_highlight_rules","ace/mode/folding/cstyle"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("./text").Mode,s=e("./abc_highlight_rules").ABCHighlightRules,o=e("./folding/cstyle").FoldMode,u=function(){this.HighlightRules=s,this.foldingRules=new o,this.$behaviour=this.$defaultBehaviour};r.inherits(u,i),function(){this.lineCommentStart="%",this.$id="ace/mode/abc",this.snippetFileId="ace/snippets/abc"}.call(u.prototype),t.Mode=u}); (function() {
ace.require(["ace/mode/abc"], function(m) {
if (typeof module == "object" && typeof exports == "object" && module) {
module.exports = m;
}
});
})();
\ No newline at end of file
ace.define("ace/mode/yaml_highlight_rules",["require","exports","module","ace/lib/oop","ace/mode/text_highlight_rules"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("./text_highlight_rules").TextHighlightRules,s=function(){this.$rules={start:[{token:"comment",regex:"#.*$"},{token:"list.markup",regex:/^(?:-{3}|\.{3})\s*(?=#|$)/},{token:"list.markup",regex:/^\s*[\-?](?:$|\s)/},{token:"constant",regex:"!![\\w//]+"},{token:"constant.language",regex:"[&\\*][a-zA-Z0-9-_]+"},{token:["meta.tag","keyword"],regex:/^(\s*\w[^\s:]*?)(:(?=\s|$))/},{token:["meta.tag","keyword"],regex:/(\w[^\s:]*?)(\s*:(?=\s|$))/},{token:"keyword.operator",regex:"<<\\w*:\\w*"},{token:"keyword.operator",regex:"-\\s*(?=[{])"},{token:"string",regex:'["](?:(?:\\\\.)|(?:[^"\\\\]))*?["]'},{token:"string",regex:/[|>][-+\d]*(?:$|\s+(?:$|#))/,onMatch:function(e,t,n,r){r=r.replace(/ #.*/,"");var i=/^ *((:\s*)?-(\s*[^|>])?)?/.exec(r)[0].replace(/\S\s*$/,"").length,s=parseInt(/\d+[\s+-]*$/.exec(r));return s?(i+=s-1,this.next="mlString"):this.next="mlStringPre",n.length?(n[0]=this.next,n[1]=i):(n.push(this.next),n.push(i)),this.token},next:"mlString"},{token:"string",regex:"['](?:(?:\\\\.)|(?:[^'\\\\]))*?[']"},{token:"constant.numeric",regex:/(\b|[+\-\.])[\d_]+(?:(?:\.[\d_]*)?(?:[eE][+\-]?[\d_]+)?)(?=[^\d-\w]|$)$/},{token:"constant.numeric",regex:/[+\-]?\.inf\b|NaN\b|0x[\dA-Fa-f_]+|0b[10_]+/},{token:"constant.language.boolean",regex:"\\b(?:true|false|TRUE|FALSE|True|False|yes|no)\\b"},{token:"paren.lparen",regex:"[[({]"},{token:"paren.rparen",regex:"[\\])}]"},{token:"text",regex:/[^\s,:\[\]\{\}]+/}],mlStringPre:[{token:"indent",regex:/^ *$/},{token:"indent",regex:/^ */,onMatch:function(e,t,n){var r=n[1];return r>=e.length?(this.next="start",n.shift(),n.shift()):(n[1]=e.length-1,this.next=n[0]="mlString"),this.token},next:"mlString"},{defaultToken:"string"}],mlString:[{token:"indent",regex:/^ *$/},{token:"indent",regex:/^ */,onMatch:function(e,t,n){var r=n[1];return r>=e.length?(this.next="start",n.splice(0)):this.next="mlString",this.token},next:"mlString"},{token:"string",regex:".+"}]},this.normalizeRules()};r.inherits(s,i),t.YamlHighlightRules=s}),ace.define("ace/mode/matching_brace_outdent",["require","exports","module","ace/range"],function(e,t,n){"use strict";var r=e("../range").Range,i=function(){};(function(){this.checkOutdent=function(e,t){return/^\s+$/.test(e)?/^\s*\}/.test(t):!1},this.autoOutdent=function(e,t){var n=e.getLine(t),i=n.match(/^(\s*\})/);if(!i)return 0;var s=i[1].length,o=e.findMatchingBracket({row:t,column:s});if(!o||o.row==t)return 0;var u=this.$getIndent(e.getLine(o.row));e.replace(new r(t,0,t,s-1),u)},this.$getIndent=function(e){return e.match(/^\s*/)[0]}}).call(i.prototype),t.MatchingBraceOutdent=i}),ace.define("ace/mode/folding/coffee",["require","exports","module","ace/lib/oop","ace/mode/folding/fold_mode","ace/range"],function(e,t,n){"use strict";var r=e("../../lib/oop"),i=e("./fold_mode").FoldMode,s=e("../../range").Range,o=t.FoldMode=function(){};r.inherits(o,i),function(){this.getFoldWidgetRange=function(e,t,n){var r=this.indentationBlock(e,n);if(r)return r;var i=/\S/,o=e.getLine(n),u=o.search(i);if(u==-1||o[u]!="#")return;var a=o.length,f=e.getLength(),l=n,c=n;while(++n<f){o=e.getLine(n);var h=o.search(i);if(h==-1)continue;if(o[h]!="#")break;c=n}if(c>l){var p=e.getLine(c).length;return new s(l,a,c,p)}},this.getFoldWidget=function(e,t,n){var r=e.getLine(n),i=r.search(/\S/),s=e.getLine(n+1),o=e.getLine(n-1),u=o.search(/\S/),a=s.search(/\S/);if(i==-1)return e.foldWidgets[n-1]=u!=-1&&u<a?"start":"","";if(u==-1){if(i==a&&r[i]=="#"&&s[i]=="#")return e.foldWidgets[n-1]="",e.foldWidgets[n+1]="","start"}else if(u==i&&r[i]=="#"&&o[i]=="#"&&e.getLine(n-2).search(/\S/)==-1)return e.foldWidgets[n-1]="start",e.foldWidgets[n+1]="","";return u!=-1&&u<i?e.foldWidgets[n-1]="start":e.foldWidgets[n-1]="",i<a?"start":""}}.call(o.prototype)}),ace.define("ace/mode/yaml",["require","exports","module","ace/lib/oop","ace/mode/text","ace/mode/yaml_highlight_rules","ace/mode/matching_brace_outdent","ace/mode/folding/coffee","ace/worker/worker_client"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("./text").Mode,s=e("./yaml_highlight_rules").YamlHighlightRules,o=e("./matching_brace_outdent").MatchingBraceOutdent,u=e("./folding/coffee").FoldMode,a=e("../worker/worker_client").WorkerClient,f=function(){this.HighlightRules=s,this.$outdent=new o,this.foldingRules=new u,this.$behaviour=this.$defaultBehaviour};r.inherits(f,i),function(){this.lineCommentStart=["#"],this.getNextLineIndent=function(e,t,n){var r=this.$getIndent(t);if(e=="start"){var i=t.match(/^.*[\{\(\[]\s*$/);i&&(r+=n)}return r},this.checkOutdent=function(e,t,n){return this.$outdent.checkOutdent(t,n)},this.autoOutdent=function(e,t,n){this.$outdent.autoOutdent(t,n)},this.createWorker=function(e){var t=new a(["ace"],"ace/mode/yaml_worker","YamlWorker");return t.attachToDocument(e.getDocument()),t.on("annotate",function(t){e.setAnnotations(t.data)}),t.on("terminate",function(){e.clearAnnotations()}),t},this.$id="ace/mode/yaml"}.call(f.prototype),t.Mode=f}); (function() {
ace.require(["ace/mode/yaml"], function(m) {
if (typeof module == "object" && typeof exports == "object" && module) {
module.exports = m;
}
});
})();
\ No newline at end of file
ace.define("ace/snippets/django",["require","exports","module"],function(e,t,n){"use strict";t.snippetText="# Model Fields\n\n# Note: Optional arguments are using defaults that match what Django will use\n# as a default, e.g. with max_length fields. Doing this as a form of self\n# documentation and to make it easy to know whether you should override the\n# default or not.\n\n# Note: Optional arguments that are booleans will use the opposite since you\n# can either not specify them, or override them, e.g. auto_now_add=False.\n\nsnippet auto\n ${1:FIELDNAME} = models.AutoField(${2})\nsnippet bool\n ${1:FIELDNAME} = models.BooleanField(${2:default=True})\nsnippet char\n ${1:FIELDNAME} = models.CharField(max_length=${2}${3:, blank=True})\nsnippet comma\n ${1:FIELDNAME} = models.CommaSeparatedIntegerField(max_length=${2}${3:, blank=True})\nsnippet date\n ${1:FIELDNAME} = models.DateField(${2:auto_now_add=True, auto_now=True}${3:, blank=True, null=True})\nsnippet datetime\n ${1:FIELDNAME} = models.DateTimeField(${2:auto_now_add=True, auto_now=True}${3:, blank=True, null=True})\nsnippet decimal\n ${1:FIELDNAME} = models.DecimalField(max_digits=${2}, decimal_places=${3})\nsnippet email\n ${1:FIELDNAME} = models.EmailField(max_length=${2:75}${3:, blank=True})\nsnippet file\n ${1:FIELDNAME} = models.FileField(upload_to=${2:path/for/upload}${3:, max_length=100})\nsnippet filepath\n ${1:FIELDNAME} = models.FilePathField(path=${2:\"/abs/path/to/dir\"}${3:, max_length=100}${4:, match=\"*.ext\"}${5:, recursive=True}${6:, blank=True, })\nsnippet float\n ${1:FIELDNAME} = models.FloatField(${2})\nsnippet image\n ${1:FIELDNAME} = models.ImageField(upload_to=${2:path/for/upload}${3:, height_field=height, width_field=width}${4:, max_length=100})\nsnippet int\n ${1:FIELDNAME} = models.IntegerField(${2})\nsnippet ip\n ${1:FIELDNAME} = models.IPAddressField(${2})\nsnippet nullbool\n ${1:FIELDNAME} = models.NullBooleanField(${2})\nsnippet posint\n ${1:FIELDNAME} = models.PositiveIntegerField(${2})\nsnippet possmallint\n ${1:FIELDNAME} = models.PositiveSmallIntegerField(${2})\nsnippet slug\n ${1:FIELDNAME} = models.SlugField(max_length=${2:50}${3:, blank=True})\nsnippet smallint\n ${1:FIELDNAME} = models.SmallIntegerField(${2})\nsnippet text\n ${1:FIELDNAME} = models.TextField(${2:blank=True})\nsnippet time\n ${1:FIELDNAME} = models.TimeField(${2:auto_now_add=True, auto_now=True}${3:, blank=True, null=True})\nsnippet url\n ${1:FIELDNAME} = models.URLField(${2:verify_exists=False}${3:, max_length=200}${4:, blank=True})\nsnippet xml\n ${1:FIELDNAME} = models.XMLField(schema_path=${2:None}${3:, blank=True})\n# Relational Fields\nsnippet fk\n ${1:FIELDNAME} = models.ForeignKey(${2:OtherModel}${3:, related_name=''}${4:, limit_choices_to=}${5:, to_field=''})\nsnippet m2m\n ${1:FIELDNAME} = models.ManyToManyField(${2:OtherModel}${3:, related_name=''}${4:, limit_choices_to=}${5:, symmetrical=False}${6:, through=''}${7:, db_table=''})\nsnippet o2o\n ${1:FIELDNAME} = models.OneToOneField(${2:OtherModel}${3:, parent_link=True}${4:, related_name=''}${5:, limit_choices_to=}${6:, to_field=''})\n\n# Code Skeletons\n\nsnippet form\n class ${1:FormName}(forms.Form):\n \"\"\"${2:docstring}\"\"\"\n ${3}\n\nsnippet model\n class ${1:ModelName}(models.Model):\n \"\"\"${2:docstring}\"\"\"\n ${3}\n \n class Meta:\n ${4}\n \n def __unicode__(self):\n ${5}\n \n def save(self, force_insert=False, force_update=False):\n ${6}\n \n @models.permalink\n def get_absolute_url(self):\n return ('${7:view_or_url_name}' ${8})\n\nsnippet modeladmin\n class ${1:ModelName}Admin(admin.ModelAdmin):\n ${2}\n \n admin.site.register($1, $1Admin)\n \nsnippet tabularinline\n class ${1:ModelName}Inline(admin.TabularInline):\n model = $1\n\nsnippet stackedinline\n class ${1:ModelName}Inline(admin.StackedInline):\n model = $1\n\nsnippet r2r\n return render_to_response('${1:template.html}', {\n ${2}\n }${3:, context_instance=RequestContext(request)}\n )\n",t.scope="django"}); (function() {
ace.require(["ace/snippets/django"], function(m) {
if (typeof module == "object" && typeof exports == "object" && module) {
module.exports = m;
}
});
})();
\ No newline at end of file
; (function() {
ace.require(["ace/snippets/yaml"], function(m) {
if (typeof module == "object" && typeof exports == "object" && module) {
module.exports = m;
}
});
})();
\ No newline at end of file
var Websock_native; // not sure var Websock_native; // not sure
$(function() { $(function() {
var meta_data = ace.edit('ace-meta-data', {
mode: "ace/mode/yaml", useWorker: false,
selectionStyle: "text" });
var textarea_meta = $('textarea[name="ci_meta_data"]');
meta_data.getSession().setValue(textarea_meta.val())
meta_data.getSession().on("change", function () {
textarea_meta.val(meta_data.getSession().getValue());
});
var user_data = ace.edit('ace-user-data', {
mode: "ace/mode/yaml", useWorker: false,
selectionStyle: "text" });
var textarea_user = $('textarea[name="ci_user_data"]');
user_data.getSession().setValue(textarea_user.val())
user_data.getSession().on("change", function () {
textarea_user.val(user_data.getSession().getValue());
});
meta_data.session.setTabSize(4);
meta_data.session.setUseSoftTabs(true);
user_data.session.setTabSize(4);
user_data.session.setUseSoftTabs(true);
document.getElementById('ace-meta-data').style.fontSize='14px';
document.getElementById('ace-user-data').style.fontSize='14px';
/* */
$('#vm-details-cidata-save').click(function(e) {
$.ajax({
type: 'POST',
url: $(this).parents("form").prop('action'),
data: $('#resource-cidata-form').serialize(),
success: function(data, textStatus, xhr) {
if(data.success) {
$('a[href="#activity"]').trigger("click");
} else {
addMessage(data.messages.join("<br />"), "danger");
}
$("#vm-details-resources-save i").removeClass('fa-refresh fa-spin').addClass("fa-floppy-o");
},
error: function(xhr, textStatus, error) {
$("#vm-details-resources-save i").removeClass('fa-refresh fa-spin').addClass("fa-floppy-o");
if (xhr.status == 500) {
addMessage("500 Internal Server Error", "danger");
} else {
addMessage(xhr.status + " Unknown Error", "danger");
}
}
});
e.preventDefault()
})
/* save resources */ /* save resources */
$('#vm-details-resources-save').click(function(e) { $('#vm-details-resources-save').click(function(e) {
var error = false; var error = false;
......
/* Move down content because we have a fixed navbar that is 50px tall */
body {
padding-top: 50px;
padding-bottom: 20px;
margin-bottom: 30px;
/* sticky footer */
}
html {
position: relative;
min-height: 100%;
}
/* Set widths on the navbar form inputs since otherwise they're 100% wide */
.navbar-form input[type="text"],
.navbar-form input[type="password"] {
width: 180px;
}
/* Wrapping element */
/* Set some basic padding to keep content from hitting the edges */
.body-content {
padding-left: 15px;
padding-right: 15px;
}
/* Responsive: Portrait tablets and up */
@media screen and (min-width: 768px) {
/* Let the jumbotron breathe */
.container > :first-child {
margin-top: 20px;
}
/* Remove padding from wrapping element since we kick in the grid classes here */
.body-content {
padding: 0;
}
.navbar-nav > li > a {
padding-top: 12.5px;
padding-bottom: 12.5px;
}
}
.no-margin {
margin: 0!important;
}
.list-group .list-group-footer {
padding-top: 5px;
padding-bottom: 5px;
}
.big {
font-size: 2em;
}
.bigbig {
font-size: 3em;
}
.big-tag {
font-size: 1.2em;
}
/* small buttons for tags, copied from Bootstraps input-sm, bnt-sm */
.btn-tags,
.btn-traits {
padding: 3px 6px;
font-size: 11px;
line-height: 1.5;
border-radius: 3px;
}
.input-tags,
.input-tratis {
height: 22px;
padding: 2px 8px;
font-size: 11px;
line-height: 1.5;
border-radius: 3px;
}
/* font awesome font */
.font-awesome-font {
font-family: "FontAwesome";
}
.nojs-dropdown-menu {
position: absolute;
display: none;
z-index: 1;
}
.nojs-dropdown-toggle:focus + .nojs-dropdown-menu {
display: block;
}
.nojs-dropdown-toggle:focus {
outline: none;
}
.nojs-dropdown-menu:hover {
display: block;
}
/* footer */
footer {
position: absolute;
bottom: 0;
width: 100%;
/* Set the fixed height of the footer here */
height: 30px;
background-color: #101010;
color: white;
font-size: 13px;
padding: 5px 5px 0 5px;
box-shadow: 0 0 30px rgba(0, 0, 0, 0.4);
text-align: center;
}
footer a,
footer a:hover,
footer a:visited {
color: white;
text-decoration: underline;
}
.table-sorting {
display: none;
}
/* 2px border bottom for all bootstrap tables */
.table thead > tr > th {
border-bottom: 1px;
}
.badge-pulse {
-webkit-animation-name: 'pulse_animation';
-webkit-animation-duration: 1000ms;
-webkit-transform-origin: 70% 70%;
-webkit-animation-iteration-count: infinite;
-webkit-animation-timing-function: linear;
}
@-webkit-keyframes pulse_animation {
0% {
-webkit-transform: scale(1);
}
30% {
-webkit-transform: scale(1);
}
40% {
-webkit-transform: scale(1.18);
}
50% {
-webkit-transform: scale(1);
}
60% {
-webkit-transform: scale(1);
}
70% {
-webkit-transform: scale(1.08);
}
80% {
-webkit-transform: scale(1);
}
100% {
-webkit-transform: scale(1);
}
}
.btn-toolbar {
margin-bottom: 5px;
}
.vm-details-home-edit-description {
font-size: 85%;
/* ~ small tag */
}
{% load i18n %}
<p class="text-right">
<a class="btn btn-primary" data-toggle="collapse" href="#collapseExample" role="button" aria-expanded="false" aria-controls="collapseExample">
Cloud-init options
</a>
</p>
<div class="collapse" id="collapseExample">
<div class="card card-body">
<b> {% trans "Use Jinja2 template syntax, values: " %}</b>
<ul>
<li>{% trans "<code>hostname</code> - vm hostname" %}</li>
<li>{% trans "<code>sysuser</code> - vm system default user (cloud)" %}</li>
<li>{% trans "<code>password</code> - default user password (use 'hash' filter to convert sha512)" %}</li>
<li>{% trans "<code>owner</code> - vm owner django username" %}</li>
<li>{% trans "<code>acl.allusers</code> - get all associated user's username to vm (list)" %}</li>
<li>{% trans "<code>acl.operators</code> - get all associated operator level user's username to vm (list)" %}</li>
<li>{% trans "<code>acl.users</code> - get all associated user level user's username to vm (list)" %}</li>
<li>{% trans "<code>net.ipv4</code> - default host ip address" %}</li>
<li>{% trans "<code>net.ipv6</code> - default host ipv6 address" %}</li>
<li>{% trans "<code>net.vlans</code> - associated vlans: list of objects (name: vlan name, ipv4/ipv6: host ip in vlan)" %}</li>
<li>{% trans "<code>ssh.keys</code> - owner's ssh-keys dictionary: the key is the ssh-key's name" %}</li>
<li>{% trans "<code>ci.rndstr(len: int)</code> - function: make random string with 'len' charachters lenght" %}</li>
</ul>
<p>{% trans "Filters: hash - sha512 hash method" %}</p>
<b>Example:</b>
<pre>{% verbatim %}
#cloud-config
users:
- default
- name: {{sysuser}}
sudo: ['ALL=(ALL) NOPASSWD:ALL']
groups: sudo
shell: /bin/bash
ssh_pwauth: True
chpasswd: { expire: False }
lock-passwd: false
passwd: "{{ password | hash }}"
ssh_authorized_keys:
- {{ ssh.keys['my-key'] }}
{% for u in acl.operators %}
- name: {{i}}
shell: /bin/bash
{% endfor %}
{% endverbatim %}</pre>
</div>
</div>
\ No newline at end of file
...@@ -2,6 +2,9 @@ ...@@ -2,6 +2,9 @@
{% load sizefieldtags %} {% load sizefieldtags %}
<i class="fa fa-file"></i> <i class="fa fa-file"></i>
{% if d.ci_disk %} <i class="fa fa-cloud-upload"></i>
{% endif %}
{{ d.name }} (#{{ d.id }}) - {{ d.size|filesize }} {{ d.name }} (#{{ d.id }}) - {{ d.size|filesize }}
......
{% load i18n %} {% load i18n %}
<form action="{{ acl.url }}" method="post">{% csrf_token %} <form action="{{ acl.url }}" method="post">{% csrf_token %}
<table class="table table-striped table-with-form-fields acl-table" id="{{table_id}}"> <table class="table table-striped table-with-form-fields acl-table" id="{{table_id}}">
<thead> <thead>
...@@ -58,7 +59,7 @@ ...@@ -58,7 +59,7 @@
</tr> </tr>
{% endfor %} {% endfor %}
<tr><td><i class="fa fa-plus"></i></td> <tr><td><i class="fa fa-plus"></i></td>
<td>{{aclform.name }}</td> <td>{{ aclform.name }}</td>
<td><select class="form-control" name="level"> <td><select class="form-control" name="level">
{% for id, name in acl.levels %} {% for id, name in acl.levels %}
{% if id in acl.allowed_levels %} {% if id in acl.allowed_levels %}
......
...@@ -26,6 +26,15 @@ ...@@ -26,6 +26,15 @@
{{ form.req_traits|as_crispy_field }} {{ form.req_traits|as_crispy_field }}
{{ form.description|as_crispy_field }} {{ form.description|as_crispy_field }}
{{ form.system|as_crispy_field }} {{ form.system|as_crispy_field }}
<hr/>
{% include "dashboard/_ci-data-help.html" %}
<div class="alert alert-warning">
{% trans "Template validition only works on a real virtual machine" %}
</div>
{{ form.cloud_init|as_crispy_field }}
{{ form.ci_meta_data|as_crispy_field }}
{{ form.ci_user_data|as_crispy_field }}
</fieldset> </fieldset>
<fieldset> <fieldset>
<legend>{% trans "External resources" %}</legend> <legend>{% trans "External resources" %}</legend>
......
...@@ -143,12 +143,6 @@ ...@@ -143,12 +143,6 @@
{% if user.is_superuser %} {% if user.is_superuser %}
<hr /> <hr />
<script type="text/javascript" src="/static/admin/js/vendor/jquery/jquery.min.js"></script>
<script type="text/javascript" src="/static/admin/js/jquery.init.js"></script>
<script type="text/javascript" src="/static/autocomplete_light/jquery.init.js"></script>
<script type="text/javascript" src="/static/autocomplete_light/vendor/select2/dist/js/select2.js"></script>
{{ group_perm_form.media }}
<h3>{% trans "Group permissions" %}</h3> <h3>{% trans "Group permissions" %}</h3>
<div id="group-detail-permissions"> <div id="group-detail-permissions">
......
...@@ -52,6 +52,14 @@ ...@@ -52,6 +52,14 @@
{{ form.description|as_crispy_field }} {{ form.description|as_crispy_field }}
{{ form.system|as_crispy_field }} {{ form.system|as_crispy_field }}
{{ form.has_agent|as_crispy_field }} {{ form.has_agent|as_crispy_field }}
<hr/>
{% include "dashboard/_ci-data-help.html" %}
<div class="alert alert-warning">
{% trans "Template validition only works on a real virtual machine" %}
</div>
{{ form.cloud_init|as_crispy_field }}
{{ form.ci_meta_data|as_crispy_field }}
{{ form.ci_user_data|as_crispy_field }}
</fieldset> </fieldset>
<fieldset> <fieldset>
<legend>{% trans "External resources" %}</legend> <legend>{% trans "External resources" %}</legend>
......
...@@ -227,6 +227,11 @@ ...@@ -227,6 +227,11 @@
{% trans "Network" %}</a> {% trans "Network" %}</a>
</li> </li>
<li> <li>
<a href="#cloudinit" data-toggle="pill" data-target="#_cloudinit" class="text-center">
<i class="fa fa-cloud-upload fa-2x"></i><br>
{% trans "Cloud-init" %}</a>
</li>
<li>
<a href="#activity" data-toggle="pill" data-target="#_activity" class="text-center" <a href="#activity" data-toggle="pill" data-target="#_activity" class="text-center"
data-activity-url="{% url "dashboard.views.vm-activity-list" instance.pk %}"> data-activity-url="{% url "dashboard.views.vm-activity-list" instance.pk %}">
<i class="fa fa-clock-o fa-2x"></i><br> <i class="fa fa-clock-o fa-2x"></i><br>
...@@ -244,6 +249,8 @@ ...@@ -244,6 +249,8 @@
<hr class="js-hidden"/> <hr class="js-hidden"/>
<div class="not-tab-pane" id="_network">{% include "dashboard/vm-detail/network.html" %}</div> <div class="not-tab-pane" id="_network">{% include "dashboard/vm-detail/network.html" %}</div>
<hr class="js-hidden"/> <hr class="js-hidden"/>
<div class="not-tab-pane" id="_cloudinit">{% include "dashboard/vm-detail/cloudinit.html" %}</div>
<hr class="js-hidden"/>
<div class="not-tab-pane" id="_activity">{% include "dashboard/vm-detail/activity.html" %}</div> <div class="not-tab-pane" id="_activity">{% include "dashboard/vm-detail/activity.html" %}</div>
<hr class="js-hidden"/> <hr class="js-hidden"/>
</div> </div>
......
{% load i18n %}
{% load sizefieldtags %}
{% load crispy_forms_tags %}
{% load static %}
{% if instance.cloud_init %}
{% include "dashboard/_ci-data-help.html" %}
<div class="row">
<div class="col-sm-12">
<h3>
{% trans "Cloud-init" %}
</h3>
<script src="{% static 'dashboard/js/ace/ace.js' %}" type="text/javascript" charset="utf-8"></script>
<form method="POST" action="{{ op.cloudinit_change.get_url }}" id="resource-cidata-form">
{% csrf_token %}
{{ ci_data.ci_meta_data|as_crispy_field }}
<div id="ace-meta-data" style="border: 1px solid #ccc;"></div>
<div style="height: 160px;"></div>
{{ ci_data.ci_user_data|as_crispy_field }}
<div id="ace-user-data" style="border: 1px solid #ccc;"></div>
<div style="height: 360px;"></div>
<button type="submit" class="btn btn-success btn-sm change-resources-button"
id="vm-details-cidata-save" data-vm="{{ instance.pk }}"
{% if not save_resources_enabled %}disabled{% endif %}>
<i class="fa fa-floppy-o"></i> {% trans "Validate & Save" %}
</button>
</form>
</div>
</div>
{% else %}
<p>Cloud-init is inactive</p>
{% endif %}
\ No newline at end of file
...@@ -79,6 +79,7 @@ ...@@ -79,6 +79,7 @@
</div> </div>
{% endif %} {% endif %}
{% if user.is_superuser %} {% if user.is_superuser %}
<hr/> <hr/>
......
...@@ -116,7 +116,8 @@ urlpatterns = [ ...@@ -116,7 +116,8 @@ urlpatterns = [
name='dashboard.views.vm-raw-data'), name='dashboard.views.vm-raw-data'),
url(r'^vm/(?P<pk>\d+)/toggle_tutorial/$', toggle_template_tutorial, url(r'^vm/(?P<pk>\d+)/toggle_tutorial/$', toggle_template_tutorial,
name='dashboard.views.vm-toggle-tutorial'), name='dashboard.views.vm-toggle-tutorial'),
#url(r'^vm/(?P<pk>\d+)/cloud_init/$', CIDataUpdate.as_view(),
# name='dashboard.views.cloud-init'),
url(r'^node/list/$', NodeList.as_view(), name='dashboard.views.node-list'), url(r'^node/list/$', NodeList.as_view(), name='dashboard.views.node-list'),
url(r'^node/(?P<pk>\d+)/$', NodeDetailView.as_view(), url(r'^node/(?P<pk>\d+)/$', NodeDetailView.as_view(),
name='dashboard.views.node-detail'), name='dashboard.views.node-detail'),
......
...@@ -17,9 +17,14 @@ ...@@ -17,9 +17,14 @@
from django.core.exceptions import ValidationError from django.core.exceptions import ValidationError
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
import jinja2
from lxml import etree as ET from lxml import etree as ET
import logging import logging
import yaml
from vm.models import Instance
rng_file = "/usr/share/libvirt/schemas/domain.rng" rng_file = "/usr/share/libvirt/schemas/domain.rng"
...@@ -50,6 +55,42 @@ def domain_validator(value): ...@@ -50,6 +55,42 @@ def domain_validator(value):
raise ValidationError(e.message) raise ValidationError(e.message)
def meta_data_validator(instance, value):
try:
instance.validate_ci_data(value)
except yaml.YAMLError as exc:
if hasattr(exc, 'problem_mark'):
if exc.context != None:
raise ValidationError(' parser says\n' + str(exc.problem_mark) + '\n ' +
str(exc.problem) + ' ' + str(exc.context) +
'\nPlease correct data and retry.')
else:
raise ValidationError(' parser says\n' + str(exc.problem_mark) + '\n ' +
str(exc.problem) + '\nPlease correct data and retry.')
else:
raise ValidationError("Something went wrong while parsing yaml file")
except jinja2.exceptions.TemplateError as exc:
raise ValidationError(exc.message)
def user_data_validator(instance, value):
try:
instance.validate_ci_data(value)
except yaml.YAMLError as exc:
if hasattr(exc, 'problem_mark'):
if exc.context != None:
raise ValidationError(' parser says\n' + str(exc.problem_mark) + '\n ' +
str(exc.problem) + ' ' + str(exc.context) +
'\nPlease correct data and retry.')
else:
raise ValidationError(' parser says\n' + str(exc.problem_mark) + '\n ' +
str(exc.problem) + '\nPlease correct data and retry.')
else:
raise ValidationError("Something went wrong while parsing yaml file")
except jinja2.exceptions.TemplateError as exc:
raise ValidationError(exc.message)
def connect_command_template_validator(value): def connect_command_template_validator(value):
"""Validate value as a connect command template. """Validate value as a connect command template.
......
...@@ -164,6 +164,7 @@ class TemplateCreate(SuccessMessageMixin, CreateView): ...@@ -164,6 +164,7 @@ class TemplateCreate(SuccessMessageMixin, CreateView):
tags = post.pop("tags") tags = post.pop("tags")
post['pw'] = User.objects.make_random_password() post['pw'] = User.objects.make_random_password()
post['is_base'] = True post['is_base'] = True
inst = Instance.create(params=post, disks=[], inst = Instance.create(params=post, disks=[],
networks=networks, networks=networks,
tags=tags, req_traits=req_traits) tags=tags, req_traits=req_traits)
......
...@@ -60,7 +60,7 @@ from .util import ( ...@@ -60,7 +60,7 @@ from .util import (
TransferOwnershipConfirmView, TransferOwnershipView, TransferOwnershipConfirmView, TransferOwnershipView,
) )
from ..forms import ( from ..forms import (
AclUserOrGroupAddForm, VmResourcesForm, TraitsForm, RawDataForm, AclUserOrGroupAddForm, CIDataForm, VmResourcesForm, TraitsForm, RawDataForm,
VmAddInterfaceForm, VmCreateDiskForm, VmDownloadDiskForm, VmAddInterfaceForm, VmCreateDiskForm, VmDownloadDiskForm,
VmImportDiskForm, VmSaveForm, VmImportDiskForm, VmSaveForm,
VmRenewForm, VmStateChangeForm, VmListSearchForm, VmCustomizeForm, VmRenewForm, VmStateChangeForm, VmListSearchForm, VmCustomizeForm,
...@@ -70,6 +70,7 @@ from ..forms import ( ...@@ -70,6 +70,7 @@ from ..forms import (
VmRemoveInterfaceForm, VmRemoveInterfaceForm,
VmRenameForm, VmRenameForm,
) )
from django.views.generic.edit import FormMixin
from request.models import TemplateAccessType, LeaseType from request.models import TemplateAccessType, LeaseType
from request.forms import LeaseRequestForm, TemplateRequestForm from request.forms import LeaseRequestForm, TemplateRequestForm
from ..models import Favourite from ..models import Favourite
...@@ -145,6 +146,8 @@ class VmDetailView(GraphMixin, CheckedDetailView): ...@@ -145,6 +146,8 @@ class VmDetailView(GraphMixin, CheckedDetailView):
'connect_commands': user.profile.get_connect_commands(instance), 'connect_commands': user.profile.get_connect_commands(instance),
'hide_tutorial': hide_tutorial, 'hide_tutorial': hide_tutorial,
'fav': instance.favourite_set.filter(user=user).exists(), 'fav': instance.favourite_set.filter(user=user).exists(),
# 'ci_data_mapping' : json.dumps(instance.get_ci_data_dict()),
'ci_key': '{{key}}'
}) })
# activity data # activity data
...@@ -204,6 +207,8 @@ class VmDetailView(GraphMixin, CheckedDetailView): ...@@ -204,6 +207,8 @@ class VmDetailView(GraphMixin, CheckedDetailView):
"PENDING", "PENDING",
) )
context['ci_data'] = CIDataForm(instance=instance)
return context return context
def post(self, request, *args, **kwargs): def post(self, request, *args, **kwargs):
...@@ -545,6 +550,37 @@ class VmSaveView(FormOperationMixin, VmOperationView): ...@@ -545,6 +550,37 @@ class VmSaveView(FormOperationMixin, VmOperationView):
val['clone'] = True val['clone'] = True
return val return val
class CIDataUpdate(VmOperationView):
op = 'cloudinit_change'
icon = "cloud-upload"
show_in_toolbar = False
wait_for_result = 0.5
def post(self, request, extra=None, *args, **kwargs):
if extra is None:
extra = {}
instance = get_object_or_404(Instance, pk=kwargs['pk'])
form = CIDataForm(request.POST, instance=instance)
if not form.is_valid():
for f in form.errors:
messages.error(request, "<strong>%s</strong>: %s" % (
f, form.errors[f].as_text()
))
if request.is_ajax(): # this is not too nice
store = messages.get_messages(request)
store.used = True
return JsonResponse({'success': False,
'messages': [str(m) for m in store]})
else:
return HttpResponseRedirect(instance.get_absolute_url() +
"#cloudinit")
else:
extra = form.cleaned_data
return super(CIDataUpdate, self).post(request, extra,
*args, **kwargs)
class VmResourcesChangeView(VmOperationView): class VmResourcesChangeView(VmOperationView):
op = 'resources_change' op = 'resources_change'
...@@ -800,6 +836,7 @@ vm_ops = OrderedDict([ ...@@ -800,6 +836,7 @@ vm_ops = OrderedDict([
('add_port', VmPortAddView), ('add_port', VmPortAddView),
('renew', VmRenewView), ('renew', VmRenewView),
('resources_change', VmResourcesChangeView), ('resources_change', VmResourcesChangeView),
('cloudinit_change', CIDataUpdate),
('password_reset', VmOperationView.factory( ('password_reset', VmOperationView.factory(
op='password_reset', icon='unlock', effect='warning', op='password_reset', icon='unlock', effect='warning',
show_in_toolbar=False, wait_for_result=0.5, with_reload=True)), show_in_toolbar=False, wait_for_result=0.5, with_reload=True)),
......
# Generated by Django 3.2.3 on 2022-07-20 12:54
from django.db import migrations, models
import firewall.fields
class Migration(migrations.Migration):
dependencies = [
('firewall', '0007_auto_20211102_1331'),
]
operations = [
migrations.AlterField(
model_name='vlan',
name='ipv6_template',
field=models.TextField(blank=True, help_text='Template for translating IPv4 addresses to IPv6. Automatically generated hosts in dual-stack networks will get this address. The template can contain four tokens: "%(a)d", "%(b)d", "%(c)d", and "%(d)d", representing the four unicode of the IPv4 address, respectively, in decimal notation. Moreover you can use any standard printf format specification like %(a)02x to get the first byte as two hexadecimal digits. Usual choices for mapping 198.51.100.0/24 to 2001:0DB8:1:1::/64 would be "2001:db8:1:1:%(d)d::" and "2001:db8:1:1:%(d)02x00::".', validators=[firewall.fields.val_ipv6_template], verbose_name='ipv6 template'),
),
migrations.AlterField(
model_name='vlan',
name='reverse_domain',
field=models.TextField(default='%(d)d.%(c)d.%(b)d.%(a)d.in-addr.arpa', help_text='Template of the IPv4 reverse domain name that should be generated for each host. The template should contain four tokens: "%(a)d", "%(b)d", "%(c)d", and "%(d)d", representing the four unicode of the address, respectively, in decimal notation. For example, the template for the standard reverse address is: "%(d)d.%(c)d.%(b)d.%(a)d.in-addr.arpa".', validators=[firewall.fields.val_reverse_domain], verbose_name='reverse domain'),
),
]
.table-with-form-fields tbody tr td {
line-height: 34px;
}
#vlan-access-table th:last-child,
#vlan-access-table td:last-child {
text-align: center;
}
#host-detail-records-table td:first-child,
#host-detail-records-table th:first-child {
text-align: center;
width: 60px;
}
body {
padding-top: 40px;
}
/* note: this doesn't really work */
a i:hover {
text-decoration: none;
}
footer {
margin-top: 45px;
}
.messagelist {
margin-top: 25px;
}
#rule-list-table td {
text-align: center;
}
#rule-list-table td:nth-child(2),
#rule-list-table td:nth-child(3) {
text-align: left;
}
.table-responsive {
margin-top: 15px;
}
#network-host-list-form {
margin-top: 6px;
}
# Generated by Django 3.2.3 on 2022-07-21 14:48
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('storage', '0003_auto_20200424_2000'),
]
operations = [
migrations.AddField(
model_name='disk',
name='ci_disk',
field=models.BooleanField(default=False),
),
]
...@@ -144,7 +144,7 @@ class Disk(TimeStampedModel): ...@@ -144,7 +144,7 @@ class Disk(TimeStampedModel):
dev_num = CharField(default='a', max_length=1, dev_num = CharField(default='a', max_length=1,
verbose_name=_("device number")) verbose_name=_("device number"))
destroyed = DateTimeField(blank=True, default=None, null=True) destroyed = DateTimeField(blank=True, default=None, null=True)
ci_disk = BooleanField(default=False)
is_ready = BooleanField(default=False) is_ready = BooleanField(default=False)
class Meta: class Meta:
...@@ -448,6 +448,27 @@ class Disk(TimeStampedModel): ...@@ -448,6 +448,27 @@ class Disk(TimeStampedModel):
return result return result
@classmethod @classmethod
def create_ci_disk(cls, meta_data, user_data, user = None, **params):
params.setdefault('name', 'ci-disk')
params.setdefault('type', 'raw-ro')
params.setdefault('size', None)
disk = cls.__create(params=params, user=user)
queue_name = disk.get_remote_queue_name('storage', priority="fast")
disk_desc = disk.get_disk_desc()
result = storage_tasks.create_ci_disk.apply_async(args=[disk_desc, meta_data, user_data],
queue=queue_name
).get(timeout=15)
disk.size = result['size']
disk.type = result['type']
disk.checksum = result['checksum']
disk.dev_num = 'c'
# disk.bus = 'virtio'
disk.is_ready = True
disk.ci_disk = True
disk.save()
return disk
@classmethod
def download(cls, url, task, user=None, **params): def download(cls, url, task, user=None, **params):
"""Create disk object and download data from url synchronusly. """Create disk object and download data from url synchronusly.
...@@ -477,6 +498,7 @@ class Disk(TimeStampedModel): ...@@ -477,6 +498,7 @@ class Disk(TimeStampedModel):
disk.type = result['type'] disk.type = result['type']
disk.checksum = result['checksum'] disk.checksum = result['checksum']
disk.is_ready = True disk.is_ready = True
disk.ci_disk = False
disk.save() disk.save()
return disk return disk
......
...@@ -33,6 +33,11 @@ def create(disk_desc): ...@@ -33,6 +33,11 @@ def create(disk_desc):
pass pass
@celery.task(name='storagedriver.create_ci_disk')
def create_ci_disk(disk_desc, meta_data, user_data):
pass
@celery.task(name='storagedriver.download') @celery.task(name='storagedriver.download')
def download(disk_desc, url): def download(disk_desc, url):
pass pass
......
# Generated by Django 3.2.3 on 2022-07-20 12:54
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('vm', '0005_auto_20211119_1017'),
]
operations = [
migrations.AddField(
model_name='instance',
name='ci_meta_data',
field=models.TextField(blank=True, help_text='When cloud-init is active, set meta-data oprtions (YAML format)', verbose_name='ci_meta_data'),
),
migrations.AddField(
model_name='instance',
name='ci_user_data',
field=models.TextField(blank=True, help_text='When cloud-init is active, set user-data oprtions (YAML format)', verbose_name='ci_user_data'),
),
migrations.AddField(
model_name='instancetemplate',
name='ci_meta_data',
field=models.TextField(blank=True, help_text='When cloud-init is active, set meta-data oprtions (YAML format)', verbose_name='ci_meta_data'),
),
migrations.AddField(
model_name='instancetemplate',
name='ci_user_data',
field=models.TextField(blank=True, help_text='When cloud-init is active, set user-data oprtions (YAML format)', verbose_name='ci_user_data'),
),
]
# Generated by Django 3.2.3 on 2022-07-20 13:12
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('vm', '0006_auto_20220720_1254'),
]
operations = [
migrations.AddField(
model_name='instance',
name='cloud_init',
field=models.BooleanField(default=True, help_text='VM use cloud-init, set user- and meta-data below', verbose_name='Use Cloud-init'),
),
migrations.AddField(
model_name='instancetemplate',
name='cloud_init',
field=models.BooleanField(default=True, help_text='VM use cloud-init, set user- and meta-data below', verbose_name='Use Cloud-init'),
),
migrations.AlterField(
model_name='instance',
name='ci_meta_data',
field=models.TextField(blank=True, help_text='When cloud-init is active, set meta-data (YAML format)', verbose_name='ci_meta_data'),
),
migrations.AlterField(
model_name='instance',
name='ci_user_data',
field=models.TextField(blank=True, help_text='When cloud-init is active, set user-data (YAML format)', verbose_name='ci_user_data'),
),
migrations.AlterField(
model_name='instancetemplate',
name='ci_meta_data',
field=models.TextField(blank=True, help_text='When cloud-init is active, set meta-data (YAML format)', verbose_name='ci_meta_data'),
),
migrations.AlterField(
model_name='instancetemplate',
name='ci_user_data',
field=models.TextField(blank=True, help_text='When cloud-init is active, set user-data (YAML format)', verbose_name='ci_user_data'),
),
]
# Generated by Django 3.2.3 on 2022-07-21 07:56
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('vm', '0007_auto_20220720_1312'),
]
operations = [
migrations.AlterField(
model_name='instance',
name='cloud_init',
field=models.BooleanField(default=False, help_text='VM use cloud-init, set user- and meta-data below', verbose_name='Use Cloud-init'),
),
migrations.AlterField(
model_name='instancetemplate',
name='cloud_init',
field=models.BooleanField(default=False, help_text='VM use cloud-init, set user- and meta-data below', verbose_name='Use Cloud-init'),
),
]
# Generated by Django 3.2.3 on 2022-07-21 11:18
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('vm', '0008_auto_20220721_0756'),
]
operations = [
migrations.AlterField(
model_name='instance',
name='ci_meta_data',
field=models.TextField(blank=True, default='empty', help_text='When cloud-init is active, set meta-data (YAML format)', verbose_name='ci_meta_data'),
),
migrations.AlterField(
model_name='instance',
name='ci_user_data',
field=models.TextField(blank=True, default='empty', help_text='When cloud-init is active, set user-data (YAML format)', verbose_name='ci_user_data'),
),
migrations.AlterField(
model_name='instancetemplate',
name='ci_meta_data',
field=models.TextField(blank=True, default='empty', help_text='When cloud-init is active, set meta-data (YAML format)', verbose_name='ci_meta_data'),
),
migrations.AlterField(
model_name='instancetemplate',
name='ci_user_data',
field=models.TextField(blank=True, default='empty', help_text='When cloud-init is active, set user-data (YAML format)', verbose_name='ci_user_data'),
),
]
...@@ -15,13 +15,16 @@ ...@@ -15,13 +15,16 @@
# You should have received a copy of the GNU General Public License along # You should have received a copy of the GNU General Public License along
# with CIRCLE. If not, see <http://www.gnu.org/licenses/>. # with CIRCLE. If not, see <http://www.gnu.org/licenses/>.
import random, string
from contextlib import contextmanager from contextlib import contextmanager
from datetime import timedelta from datetime import timedelta
from dbm import dumb
from functools import partial from functools import partial
from importlib import import_module from importlib import import_module
from ipaddress import ip_interface
from logging import getLogger from logging import getLogger
from warnings import warn from warnings import warn
from xml.dom.minidom import Text
import django.conf import django.conf
from django.contrib.auth.models import User from django.contrib.auth.models import User
...@@ -36,14 +39,21 @@ from django.urls import reverse ...@@ -36,14 +39,21 @@ from django.urls import reverse
from django.utils import timezone from django.utils import timezone
from django.utils.translation import ugettext_lazy as _, ugettext_noop from django.utils.translation import ugettext_lazy as _, ugettext_noop
import jinja2
from passlib.hash import sha512_crypt
import yaml
import re
from model_utils import Choices from model_utils import Choices
from model_utils.managers import QueryManager from model_utils.managers import QueryManager
from model_utils.models import TimeStampedModel, StatusModel from model_utils.models import TimeStampedModel, StatusModel
from taggit.managers import TaggableManager from taggit.managers import TaggableManager
from simplesshkey.models import UserKey
from django.db import models from django.db import models
from acl.models import AclBase from acl.models import AclBase
from django import forms
from common.models import ( from common.models import (
activitycontextimpl, create_readable, HumanReadableException, activitycontextimpl, create_readable, HumanReadableException,
) )
...@@ -54,6 +64,7 @@ from .common import BaseResourceConfigModel, Lease ...@@ -54,6 +64,7 @@ from .common import BaseResourceConfigModel, Lease
from .network import Interface from .network import Interface
from .node import Node, Trait from .node import Node, Trait
logger = getLogger(__name__) logger = getLogger(__name__)
pre_state_changed = Signal(providing_args=["new_state"]) pre_state_changed = Signal(providing_args=["new_state"])
post_state_changed = Signal(providing_args=["new_state"]) post_state_changed = Signal(providing_args=["new_state"])
...@@ -66,6 +77,25 @@ ACCESS_PROTOCOLS = django.conf.settings.VM_ACCESS_PROTOCOLS ...@@ -66,6 +77,25 @@ ACCESS_PROTOCOLS = django.conf.settings.VM_ACCESS_PROTOCOLS
ACCESS_METHODS = [(key, name) for key, (name, port, transport) ACCESS_METHODS = [(key, name) for key, (name, port, transport)
in list(ACCESS_PROTOCOLS.items())] in list(ACCESS_PROTOCOLS.items())]
CI_META_DATA_DEF = """
instance-id: {{ hostname }}
local-hostname: {{ hostname }}
""".strip()
CI_USER_DATA_DEF = """
#cloud-config
users:
- default
- name: {{ sysuser }}
sudo: ['ALL=(ALL) NOPASSWD:ALL']
groups: sudo
shell: /bin/bash
ssh_pwauth: True
chpasswd: { expire: False }
lock-passwd: false
passwd: "{{ password | hash }}"
""".strip()
try: try:
# Python 2: "unicode" is built-in # Python 2: "unicode" is built-in
...@@ -112,6 +142,13 @@ class VirtualMachineDescModel(BaseResourceConfigModel): ...@@ -112,6 +142,13 @@ class VirtualMachineDescModel(BaseResourceConfigModel):
verbose_name=_("Lease"), on_delete=models.CASCADE) verbose_name=_("Lease"), on_delete=models.CASCADE)
raw_data = TextField(verbose_name=_('raw_data'), blank=True, help_text=_( raw_data = TextField(verbose_name=_('raw_data'), blank=True, help_text=_(
'Additional libvirt domain parameters in XML format.')) 'Additional libvirt domain parameters in XML format.'))
cloud_init = BooleanField(verbose_name=_('Use Cloud-init'), default=False,
help_text=_(
'VM use cloud-init, set user- and meta-data below'))
ci_meta_data = TextField(verbose_name=_('CI Meta Data'), blank=True, help_text=_(
'When cloud-init is active, set meta-data (YAML format)'), default=CI_META_DATA_DEF)
ci_user_data = TextField(verbose_name=_('CI User Data'), blank=True, help_text=_(
'When cloud-init is active, set user-data (YAML format)'), default=CI_USER_DATA_DEF)
req_traits = ManyToManyField(Trait, blank=True, req_traits = ManyToManyField(Trait, blank=True,
help_text=_("A set of traits required for a " help_text=_("A set of traits required for a "
"node to declare to be suitable " "node to declare to be suitable "
...@@ -217,6 +254,47 @@ class InstanceTemplate(AclBase, VirtualMachineDescModel, TimeStampedModel): ...@@ -217,6 +254,47 @@ class InstanceTemplate(AclBase, VirtualMachineDescModel, TimeStampedModel):
return 'template.%d' % self.pk return 'template.%d' % self.pk
class CITemplate:
def rndstr(self, len):
return ''.join(random.choice(string.ascii_letters) for i in range(int(len)))
def j2_hash(value, hash='sha512'):
return sha512_crypt.hash(value)
env = jinja2.Environment(trim_blocks=True, lstrip_blocks=True)
env.filters["hash"] = j2_hash
class AclTemplate:
def __init__(self, instance):
self.user_levels = list({ 'username': u.username, 'level': l } for u, l in instance.get_users_with_level())
self.allusers = list(k['username'] for k in self.user_levels)
self.operators = list(k['username'] for k in self.user_levels if k['level'] == 'operator')
self.users = list(k['username'] for k in self.user_levels if k['level'] == 'user')
class SSHKeyTemplate:
def __init__(self, instance):
owner = instance.owner
self.keys = {}
for k in UserKey.objects.filter(user=owner):
self.keys[k.name] = k.key
class NetTemplate:
class Host:
def __init__(self, net):
self.ipv4 = str(net.host.ipv4)
self.ipv6 = str(net.host.ipv6)
self.name = str(net.vlan.name)
def __init__(self, instance):
self.vlans = list(NetTemplate.Host(net) for net in instance.interface_set.all() if net.host)
self.ipv4 = str(instance.ipv4)
self.ipv6 = str(instance.ipv6)
class Instance(AclBase, VirtualMachineDescModel, StatusModel, OperatedMixin, class Instance(AclBase, VirtualMachineDescModel, StatusModel, OperatedMixin,
TimeStampedModel): TimeStampedModel):
...@@ -333,6 +411,40 @@ class Instance(AclBase, VirtualMachineDescModel, StatusModel, OperatedMixin, ...@@ -333,6 +411,40 @@ class Instance(AclBase, VirtualMachineDescModel, StatusModel, OperatedMixin,
DeprecationWarning) DeprecationWarning)
return self.status return self.status
def get_ci_data_dict(self):
datas = {
"sysuser": "cloud",
"hostname": self.short_hostname,
"password": self.pw,
"owner": str(self.owner.username),
"net": NetTemplate(self),
"acl": AclTemplate(self),
"ssh": SSHKeyTemplate(self),
"ci": CITemplate(),
}
return datas
@property
def get_user_data(self):
data = str(self.ci_user_data)
ci_datas = self.get_ci_data_dict()
template = env.from_string(data)
return template.render(ci_datas)
@property
def get_meta_data(self):
data = str(self.ci_meta_data)
ci_datas = self.get_ci_data_dict()
template = env.from_string(data)
return template.render(ci_datas)
def validate_ci_data(self, data):
ci_datas = self.get_ci_data_dict()
template = env.from_string(data)
data = template.render(ci_datas)
yaml.dump(yaml.load(data, Loader=yaml.Loader))
return True
def _update_status(self): def _update_status(self):
"""Set the proper status of the instance to Instance.status. """Set the proper status of the instance to Instance.status.
""" """
...@@ -437,7 +549,7 @@ class Instance(AclBase, VirtualMachineDescModel, StatusModel, OperatedMixin, ...@@ -437,7 +549,7 @@ class Instance(AclBase, VirtualMachineDescModel, StatusModel, OperatedMixin,
common_fields = ['name', 'description', 'num_cores', 'ram_size', common_fields = ['name', 'description', 'num_cores', 'ram_size',
'max_ram_size', 'arch', 'priority', 'boot_menu', 'max_ram_size', 'arch', 'priority', 'boot_menu',
'raw_data', 'lease', 'access_method', 'system', 'raw_data', 'lease', 'access_method', 'system',
'has_agent'] 'cloud_init', 'ci_meta_data', 'ci_user_data', 'has_agent']
params = dict(template=template, owner=owner, pw=pwgen()) params = dict(template=template, owner=owner, pw=pwgen())
params.update([(f, getattr(template, f)) for f in common_fields]) params.update([(f, getattr(template, f)) for f in common_fields])
params.update(kwargs) # override defaults w/ user supplied values params.update(kwargs) # override defaults w/ user supplied values
......
...@@ -21,6 +21,7 @@ from io import StringIO ...@@ -21,6 +21,7 @@ from io import StringIO
from base64 import encodestring from base64 import encodestring
from hashlib import md5 from hashlib import md5
from logging import getLogger from logging import getLogger
import logging
from string import ascii_lowercase from string import ascii_lowercase
from tarfile import TarFile, TarInfo from tarfile import TarFile, TarInfo
from urllib.parse import urlsplit from urllib.parse import urlsplit
...@@ -434,6 +435,14 @@ class DeployOperation(InstanceOperation): ...@@ -434,6 +435,14 @@ class DeployOperation(InstanceOperation):
self.instance.save() self.instance.save()
else: else:
self.instance.allocate_node() self.instance.allocate_node()
init_disk = None
try:
init_disk = self.instance.disks.get(ci_disk=True)
except:
logger.debug('create init-disk')
if self.instance.cloud_init and init_disk == None:
self.instance._create_ci_image(parent_activity=activity)
# Deploy virtual images # Deploy virtual images
try: try:
...@@ -483,6 +492,25 @@ class DeployOperation(InstanceOperation): ...@@ -483,6 +492,25 @@ class DeployOperation(InstanceOperation):
node=self.instance.node) node=self.instance.node)
@register_operation @register_operation
class CreateCloudInitImage(SubOperationMixin, InstanceOperation):
id = "_create_ci_image"
name = _("create cloud-init image")
description = _("create cloud init iso from user and meta-data")
def _operation(self, user, activity, name=None):
import json
logger.info('create ci image')
disk = Disk.create_ci_disk(meta_data=self.instance.get_meta_data,
user_data=self.instance.get_user_data)
disk.full_clean()
disk.save()
self.instance.disks.add(disk)
return create_readable(ugettext_noop(
" ---- META-DATA ----\n%(meta_data)s\n\n ---- USER-DATA ----\n%(user_data)s"),
meta_data=self.instance.get_meta_data,
user_data=self.instance.get_user_data)
@register_operation
class DeployDisksOperation(SubOperationMixin, InstanceOperation): class DeployDisksOperation(SubOperationMixin, InstanceOperation):
id = "_deploy_disks" id = "_deploy_disks"
name = _("deploy disks") name = _("deploy disks")
...@@ -815,7 +843,11 @@ class SaveAsTemplateOperation(InstanceOperation): ...@@ -815,7 +843,11 @@ class SaveAsTemplateOperation(InstanceOperation):
'ram_size': self.instance.ram_size, 'ram_size': self.instance.ram_size,
'raw_data': self.instance.raw_data, 'raw_data': self.instance.raw_data,
'system': self.instance.system, 'system': self.instance.system,
'cloud_init': self.instance.cloud_init,
'ci_meta_data': self.instance.ci_meta_data,
'ci_user_data': self.instance.ci_user_data,
} }
params.update(kwargs) params.update(kwargs)
params.pop("parent_activity", None) params.pop("parent_activity", None)
...@@ -829,6 +861,7 @@ class SaveAsTemplateOperation(InstanceOperation): ...@@ -829,6 +861,7 @@ class SaveAsTemplateOperation(InstanceOperation):
self.disks = [] self.disks = []
for disk in self.instance.disks.all(): for disk in self.instance.disks.all():
if disk.ci_disk == False:
with activity.sub_activity( with activity.sub_activity(
'saving_disk', 'saving_disk',
readable_name=create_readable( readable_name=create_readable(
...@@ -1418,6 +1451,46 @@ class RecoverOperation(InstanceOperation): ...@@ -1418,6 +1451,46 @@ class RecoverOperation(InstanceOperation):
@register_operation @register_operation
class CIDataChangeOperation(InstanceOperation):
id = 'cloudinit_change'
name = _("cloud-init change")
description = _("Change cloud-init data of a stopped virtual machine.")
acl_level = "owner"
required_perms = ('vm.change_resources',)
accept_states = ('STOPPED', 'PENDING')
def _operation(self, user, activity,
ci_meta_data, ci_user_data,
with_shutdown=False, task=None):
logger.debug('save cloud-init: %s \n %s', ci_meta_data, ci_user_data)
if self.instance.status == 'RUNNING' and not with_shutdown:
raise Instance.WrongStateError(self.instance)
try:
self.instance.shutdown(parent_activity=activity, task=task)
except Instance.WrongStateError:
pass
self.instance._update_status()
self.instance.ci_meta_data = ci_meta_data
self.instance.ci_user_data = ci_user_data
self.instance.full_clean()
self.instance.save()
init_disk = None
try:
init_disk = self.instance.disks.get(ci_disk=True)
except:
logger.debug('init-disk')
if self.instance.cloud_init and init_disk != None:
self.instance.remove_disk(parent_activity=activity, disk=init_disk)
return create_readable(ugettext_noop("Meta- and user-data saved. Please redeploy the vm!"))
@register_operation
class ResourcesOperation(InstanceOperation): class ResourcesOperation(InstanceOperation):
id = 'resources_change' id = 'resources_change'
name = _("resources change") name = _("resources change")
......
...@@ -54,3 +54,4 @@ ipaddress==1.0.23 ...@@ -54,3 +54,4 @@ ipaddress==1.0.23
django-nose==1.4.7 django-nose==1.4.7
nose-exclude==0.5.0 nose-exclude==0.5.0
factory_boy==3.2.1 factory_boy==3.2.1
passlib==1.7.4
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment