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
scripts.rc
# less
*.css
# *.css
circle/dashboard/static/compile_bootstrap.css
......@@ -196,7 +196,9 @@ PIPELINE = {
"template.less",
"dashboard/dashboard.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",
),
"output_filename": "all.css",
......@@ -212,10 +214,13 @@ PIPELINE = {
"jquery-simple-slider/js/simple-slider.js",
"favico.js/favico.js",
"datatables/media/js/jquery.dataTables.js",
"autocomplete_light/jquery.init.js",
"autocomplete_light/autocomplete.init.js",
"autocomplete_light/vendor/select2/dist/js/select2.js",
# "autocomplete_light/jquery.init.js",
# "autocomplete_light/autocomplete.init.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/i18n/en.js",
"dashboard/dashboard.js",
"dashboard/activity.js",
"dashboard/group-details.js",
......@@ -239,6 +244,8 @@ PIPELINE = {
"output_filename": "all.js",
},
"vm-detail": {"source_filenames": (
"dashboard/js/ace/ace.js",
"dashboard/js/ace/mode-yaml.js",
"clipboard/dist/clipboard.min.js",
"dashboard/vm-details.js",
"no-vnc/include/util.js",
......@@ -568,7 +575,7 @@ except:
AGENT_VERSION = None
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(
'VNC_PORT_RANGE', '50000, 60000').replace(' ', '').split(',')
......
......@@ -60,7 +60,7 @@ from vm.models import (
InstanceTemplate, Lease, InterfaceTemplate, Node, Trait, Instance
)
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], ")"))
for l in LANGUAGES)
......@@ -544,7 +544,7 @@ class TemplateForm(forms.ModelForm):
else:
self.allowed_fields = (
'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
not self.instance.pk):
self.allowed_fields += tuple(set(self.fields.keys()) -
......@@ -1423,9 +1423,8 @@ class UserEditForm(forms.ModelForm):
helper.add_input(Submit("submit", _("Save")))
return helper
class AclUserOrGroupAddForm(forms.Form):
name = forms.CharField(
name = autocomplete.Select2ListChoiceField(
widget=autocomplete.ListSelect2(
url='autocomplete.acl.user-group',
attrs={'class': 'form-control',
......@@ -1434,7 +1433,7 @@ class AclUserOrGroupAddForm(forms.Form):
class TransferOwnershipForm(forms.Form):
name = forms.CharField(
name = autocomplete.Select2ListChoiceField(
widget=autocomplete.ListSelect2(
url='autocomplete.acl.user',
attrs={'class': 'form-control',
......@@ -1444,7 +1443,7 @@ class TransferOwnershipForm(forms.Form):
class AddGroupMemberForm(forms.Form):
new_member = forms.CharField(
new_member = autocomplete.Select2ListChoiceField(
widget=autocomplete.ListSelect2(
url='autocomplete.acl.user',
attrs={'class': 'form-control',
......@@ -1533,6 +1532,25 @@ class RawDataForm(forms.ModelForm):
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):
permissions = forms.ModelMultipleChoiceField(
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
$(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 */
$('#vm-details-resources-save').click(function(e) {
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 @@
{% load sizefieldtags %}
<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 }}
......
{% load i18n %}
<form action="{{ acl.url }}" method="post">{% csrf_token %}
<table class="table table-striped table-with-form-fields acl-table" id="{{table_id}}">
<thead>
......@@ -58,7 +59,7 @@
</tr>
{% endfor %}
<tr><td><i class="fa fa-plus"></i></td>
<td>{{aclform.name }}</td>
<td>{{ aclform.name }}</td>
<td><select class="form-control" name="level">
{% for id, name in acl.levels %}
{% if id in acl.allowed_levels %}
......@@ -72,4 +73,4 @@
<div class="form-actions">
<button type="submit" class="btn btn-success">{% trans "Save" %}</button>
</div>
</form>
</form>
\ No newline at end of file
......@@ -26,6 +26,15 @@
{{ form.req_traits|as_crispy_field }}
{{ form.description|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>
<legend>{% trans "External resources" %}</legend>
......
......@@ -143,12 +143,6 @@
{% if user.is_superuser %}
<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>
<div id="group-detail-permissions">
......
......@@ -52,6 +52,14 @@
{{ form.description|as_crispy_field }}
{{ form.system|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>
<legend>{% trans "External resources" %}</legend>
......
......@@ -227,6 +227,11 @@
{% trans "Network" %}</a>
</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"
data-activity-url="{% url "dashboard.views.vm-activity-list" instance.pk %}">
<i class="fa fa-clock-o fa-2x"></i><br>
......@@ -244,6 +249,8 @@
<hr class="js-hidden"/>
<div class="not-tab-pane" id="_network">{% include "dashboard/vm-detail/network.html" %}</div>
<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>
<hr class="js-hidden"/>
</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 @@
</div>
{% endif %}
{% if user.is_superuser %}
<hr/>
......
......@@ -116,7 +116,8 @@ urlpatterns = [
name='dashboard.views.vm-raw-data'),
url(r'^vm/(?P<pk>\d+)/toggle_tutorial/$', toggle_template_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/(?P<pk>\d+)/$', NodeDetailView.as_view(),
name='dashboard.views.node-detail'),
......
......@@ -17,9 +17,14 @@
from django.core.exceptions import ValidationError
from django.utils.translation import ugettext_lazy as _
import jinja2
from lxml import etree as ET
import logging
import yaml
from vm.models import Instance
rng_file = "/usr/share/libvirt/schemas/domain.rng"
......@@ -50,6 +55,42 @@ def domain_validator(value):
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):
"""Validate value as a connect command template.
......
......@@ -164,6 +164,7 @@ class TemplateCreate(SuccessMessageMixin, CreateView):
tags = post.pop("tags")
post['pw'] = User.objects.make_random_password()
post['is_base'] = True
inst = Instance.create(params=post, disks=[],
networks=networks,
tags=tags, req_traits=req_traits)
......
......@@ -60,7 +60,7 @@ from .util import (
TransferOwnershipConfirmView, TransferOwnershipView,
)
from ..forms import (
AclUserOrGroupAddForm, VmResourcesForm, TraitsForm, RawDataForm,
AclUserOrGroupAddForm, CIDataForm, VmResourcesForm, TraitsForm, RawDataForm,
VmAddInterfaceForm, VmCreateDiskForm, VmDownloadDiskForm,
VmImportDiskForm, VmSaveForm,
VmRenewForm, VmStateChangeForm, VmListSearchForm, VmCustomizeForm,
......@@ -70,6 +70,7 @@ from ..forms import (
VmRemoveInterfaceForm,
VmRenameForm,
)
from django.views.generic.edit import FormMixin
from request.models import TemplateAccessType, LeaseType
from request.forms import LeaseRequestForm, TemplateRequestForm
from ..models import Favourite
......@@ -145,6 +146,8 @@ class VmDetailView(GraphMixin, CheckedDetailView):
'connect_commands': user.profile.get_connect_commands(instance),
'hide_tutorial': hide_tutorial,
'fav': instance.favourite_set.filter(user=user).exists(),
# 'ci_data_mapping' : json.dumps(instance.get_ci_data_dict()),
'ci_key': '{{key}}'
})
# activity data
......@@ -204,6 +207,8 @@ class VmDetailView(GraphMixin, CheckedDetailView):
"PENDING",
)
context['ci_data'] = CIDataForm(instance=instance)
return context
def post(self, request, *args, **kwargs):
......@@ -545,6 +550,37 @@ class VmSaveView(FormOperationMixin, VmOperationView):
val['clone'] = True
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):
op = 'resources_change'
......@@ -800,6 +836,7 @@ vm_ops = OrderedDict([
('add_port', VmPortAddView),
('renew', VmRenewView),
('resources_change', VmResourcesChangeView),
('cloudinit_change', CIDataUpdate),
('password_reset', VmOperationView.factory(
op='password_reset', icon='unlock', effect='warning',
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):
dev_num = CharField(default='a', max_length=1,
verbose_name=_("device number"))
destroyed = DateTimeField(blank=True, default=None, null=True)
ci_disk = BooleanField(default=False)
is_ready = BooleanField(default=False)
class Meta:
......@@ -448,6 +448,27 @@ class Disk(TimeStampedModel):
return result
@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):
"""Create disk object and download data from url synchronusly.
......@@ -477,6 +498,7 @@ class Disk(TimeStampedModel):
disk.type = result['type']
disk.checksum = result['checksum']
disk.is_ready = True
disk.ci_disk = False
disk.save()
return disk
......
......@@ -33,6 +33,11 @@ def create(disk_desc):
pass
@celery.task(name='storagedriver.create_ci_disk')
def create_ci_disk(disk_desc, meta_data, user_data):
pass
@celery.task(name='storagedriver.download')
def download(disk_desc, url):
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 @@
# You should have received a copy of the GNU General Public License along
# with CIRCLE. If not, see <http://www.gnu.org/licenses/>.
import random, string
from contextlib import contextmanager
from datetime import timedelta
from dbm import dumb
from functools import partial
from importlib import import_module
from ipaddress import ip_interface
from logging import getLogger
from warnings import warn
from xml.dom.minidom import Text
import django.conf
from django.contrib.auth.models import User
......@@ -36,14 +39,21 @@ from django.urls import reverse
from django.utils import timezone
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.managers import QueryManager
from model_utils.models import TimeStampedModel, StatusModel
from taggit.managers import TaggableManager
from simplesshkey.models import UserKey
from django.db import models
from acl.models import AclBase
from django import forms
from common.models import (
activitycontextimpl, create_readable, HumanReadableException,
)
......@@ -54,6 +64,7 @@ from .common import BaseResourceConfigModel, Lease
from .network import Interface
from .node import Node, Trait
logger = getLogger(__name__)
pre_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
ACCESS_METHODS = [(key, name) for key, (name, port, transport)
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:
# Python 2: "unicode" is built-in
......@@ -112,6 +142,13 @@ class VirtualMachineDescModel(BaseResourceConfigModel):
verbose_name=_("Lease"), on_delete=models.CASCADE)
raw_data = TextField(verbose_name=_('raw_data'), blank=True, help_text=_(
'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,
help_text=_("A set of traits required for a "
"node to declare to be suitable "
......@@ -217,6 +254,47 @@ class InstanceTemplate(AclBase, VirtualMachineDescModel, TimeStampedModel):
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,
TimeStampedModel):
......@@ -333,6 +411,40 @@ class Instance(AclBase, VirtualMachineDescModel, StatusModel, OperatedMixin,
DeprecationWarning)
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):
"""Set the proper status of the instance to Instance.status.
"""
......@@ -437,7 +549,7 @@ class Instance(AclBase, VirtualMachineDescModel, StatusModel, OperatedMixin,
common_fields = ['name', 'description', 'num_cores', 'ram_size',
'max_ram_size', 'arch', 'priority', 'boot_menu',
'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.update([(f, getattr(template, f)) for f in common_fields])
params.update(kwargs) # override defaults w/ user supplied values
......
......@@ -21,6 +21,7 @@ from io import StringIO
from base64 import encodestring
from hashlib import md5
from logging import getLogger
import logging
from string import ascii_lowercase
from tarfile import TarFile, TarInfo
from urllib.parse import urlsplit
......@@ -434,6 +435,14 @@ class DeployOperation(InstanceOperation):
self.instance.save()
else:
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
try:
......@@ -482,6 +491,25 @@ class DeployOperation(InstanceOperation):
ugettext_noop("deploy vm to %(node)s"),
node=self.instance.node)
@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):
id = "_deploy_disks"
......@@ -815,7 +843,11 @@ class SaveAsTemplateOperation(InstanceOperation):
'ram_size': self.instance.ram_size,
'raw_data': self.instance.raw_data,
'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.pop("parent_activity", None)
......@@ -829,13 +861,14 @@ class SaveAsTemplateOperation(InstanceOperation):
self.disks = []
for disk in self.instance.disks.all():
with activity.sub_activity(
if disk.ci_disk == False:
with activity.sub_activity(
'saving_disk',
readable_name=create_readable(
ugettext_noop("saving disk %(name)s"),
name=disk.name)
):
self.disks.append(__try_save_disk(disk))
):
self.disks.append(__try_save_disk(disk))
# create template and do additional setup
tmpl = InstanceTemplate(**params)
......@@ -1418,6 +1451,46 @@ class RecoverOperation(InstanceOperation):
@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):
id = 'resources_change'
name = _("resources change")
......
......@@ -54,3 +54,4 @@ ipaddress==1.0.23
django-nose==1.4.7
nose-exclude==0.5.0
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