github.com/billybanfield/evergreen@v0.0.0-20170525200750-eeee692790f7/service/templates/distros.html (about) 1 {{define "scripts"}} 2 <script type="text/javascript" src="{{Static "js" "distros.js"}}?hash={{ StaticsMD5 }}"></script> 3 <script type="text/javascript"> 4 window.distros = {{ .Distros }}; 5 window.keys = {{ .Keys }}; 6 </script> 7 {{end}} 8 {{define "title"}} 9 Evergreen - Distros 10 {{end}} 11 {{define "content"}} 12 <div class="container" ng-controller="DistrosCtrl" style="margin-bottom: 15px;"> 13 <notify-box ng-init="destination='errorHeader'"></notify-box> 14 {{template "flash" . }} 15 <div ng-show="distros.length == 0"> 16 <h2>No Distros</h2> 17 <div class="row"> 18 <button type="button" ng-hide="readOnly" class="btn btn-primary" style="margin-left: 15px" ng-click="newDistro()" ng-disabled="activeDistro.new"><i class="fa fa-plus"></i>New Distro</button> 19 </div> 20 </div> 21 <div ng-form="form" class="row"> 22 <div id="nav-container" class="col-md-2" ng-show="distros.length != 0"> 23 <div> 24 <div> 25 <h2 style="text-align: center;">Distros<span ng-show="distros.length != 0">([[distros.length]])</span></h2> 26 </div> 27 <div class="row" style="text-align: center;"> 28 <button type="button" class="btn btn-primary col-lg-8" ng-hide="readOnly" style="margin-bottom: 10px; margin-left: 35px" ng-click="newDistro()" ng-disabled="activeDistro.new"><i class="fa fa-plus"></i>New Distro</button> 29 </div> 30 <div id="distros-list-container"> 31 <ul id="distros-list"> 32 <li ng-repeat="distro in distros" ng-click="form.$setPristine();setActiveDistro(distro)" 33 ng-class="{'active-distro': distro._id == activeDistro._id}"> 34 [[distro._id]] 35 </li> 36 </ul> 37 </div> 38 </div> 39 </div> 40 <div class="col-lg-2"></div> 41 <div class="col-lg-6 col-lg-offset-1" ng-show="distros.length != 0"> 42 <div ng-init="initOptions()"> 43 <div ng-show="activeDistro"> 44 <h2 style="display:inline-block; padding-right:15px">Configure 45 </h2> 46 <a class="pointer" ng-click="copyDistro()" ng-hide="hasNew||readOnly"> make a copy </a> / 47 <a ng-href="/event_log/distro/[[activeDistro._id]]"> view event log </a> 48 </div> 49 <div style="padding-top: -25px;" class="panel-body panel-default"> 50 <div> 51 <label class="distro-label">Identifier:</label> 52 <input required ng-readonly="!activeDistro.new" id="identifier" name="id" type="text" class="form-control" ng-model="activeDistro._id" placeholder="Unique identifier for this distro"> 53 <label class="icon fa fa-warning distro-error" ng-show="form.id.$error.required">Distro identifier is required<br></label> 54 <label class="icon fa fa-warning distro-error" ng-show="form.id.$dirty && form.id.$error.unsique || (activeDistro.new && form.id.$error.unique)">Distro identifier already exists</label> 55 </div> 56 <br> 57 58 <div class="panel-body panel panel-default"> 59 <div class="dropdown"> 60 <span class="distro-menu-title">Agent Architecture:</span> 61 <button class="btn btn-default dropdown-toggle" type="button" data-toggle="dropdown" aria-expanded="true" ng-disabled="readOnly"> 62 <strong class="distro-menu-item">[[activeDistro.arch | archDisplay:this]]<span class="fa fa-caret-down"></span></strong> 63 </button> 64 <ul class="dropdown-menu" style="margin-left: 125px; align: left;" role="menu"> 65 <li ng-click="form.$setDirty();setKeyValue('arch', arch.id)" required name="agentArch" ng-repeat="arch in architectures | orderBy:'display'" role="presentation"><a role="menuitem" tabindex="-1">[[arch.display]]</a></li> 66 </ul> 67 <div class="icon fa fa-warning distro-error" ng-show="form.agentArch.$dirty && form.agentArch.$error.required || form.agentArch.$invalid">Agent architecture is required</div> 68 </div> 69 <div> 70 <label class="distro-label">Working Directory:</label> 71 <input required name="workDir" type="text" class="form-control" ng-model="activeDistro.work_dir" placeholder="Absolute path in which agent runs tasks on host machine" ng-readonly="readOnly"> 72 <div class="icon fa fa-warning distro-error" ng-show="form.workDir.$dirty && form.workDir.$error.required || form.workDir.$invalid">Working Directory is required</div> 73 </div> 74 </div> 75 <br> 76 <div class="panel-body panel panel-default"> 77 <div class="dropdown"> 78 <span class="distro-menu-title">Provider:</span> 79 <button class="btn btn-default dropdown-toggle" ng-disabled="readOnly" type="button" data-toggle="dropdown"> 80 <strong class="distro-menu-item">[[activeDistro.provider | providerDisplay:this]]<span class="fa fa-caret-down"></span></strong> 81 </button> 82 <ul class="dropdown-menu" role="menu" style="margin-left: 60px; align: left;"> 83 <li ng-click="form.$setDirty();setKeyValue('provider', provider.id)" required name="providerForm" ng-repeat="provider in providers" role="presentation"><a role="menuitem" tabindex="-1">[[provider.display]]</a></li> 84 </ul> 85 <div class="icon fa fa-warning distro-error" ng-show="form.providerForm.$dirty && form.providerForm.$error.required">Distro provider is required</div> 86 </div> 87 <div ng-show="activeDistro.provider == 'docker'"> 88 <div> 89 <label class="distro-label">Host:</label> 90 <input type="text" ng-required="activeDistro.provider == 'docker'" name="hostIP" class="form-control" ng-model="activeDistro.settings.host_ip" placeholder="Machine DNS name" ng-readonly="readOnly"> 91 <div class="icon fa fa-warning distro-error" ng-show="form.hostIP.$dirty && form.hostIP.$error.required || form.hostIP.$invalid">Host DNS is required</div> 92 </div> 93 <div> 94 <label class="distro-label">Image ID:</label> 95 <input type="text" ng-readonly="readOnly" ng-required="activeDistro.provider == 'docker'" name="imageName" class="form-control" ng-model="activeDistro.settings.image_name"> 96 <div class="icon fa fa-warning distro-error" ng-show="form.imageName.$dirty && form.imageName.$error.required || form.imageName.$invalid">Image ID is required</div> 97 </div> 98 <div> 99 <label class="distro-label">Bind Address:</label> 100 <input type="text" ng-required="activeDistro.provider == 'docker'" name="bindIP" class="form-control" ng-model="activeDistro.settings.bind_ip" placeholder="e.g. localhost" ng-readonly="readOnly"> 101 <div class="icon fa fa-warning distro-error" ng-show="form.bindIP.$dirty && form.bindIP.$error.required || form.bindIP.$invalid">An address for the container to bind a port to is required</div> 102 </div> 103 104 <div> 105 <label class="distro-label">Docker Client Port:</label> 106 <input ng-readonly="readOnly" ng-required="activeDistro.provider == 'docker'" name="clientPort" class="form-control" type="number" ng-model="activeDistro.settings.client_port" placeholder="Port for connecting to docker client, e.g. 2376"> 107 <div class="icon fa fa-warning distro-error" ng-show="form.clientPort.$dirty && form.clientPort.$error.required || form.clientPort.$invalid || form.clientPort.$modelValue < 0">Non-negative numeric Client Port is required</div> 108 </div> 109 <div id="port-table" class="distro-table-scroll"> 110 <label class="distro-label">Container Port Range:</label> 111 <table style="margin-left: -8px;" ng-form name="portRange" class="table distro-table" ng-init="form.devName=''; form.devSize=''"> 112 <tr> 113 <td style="padding-left: 10px;"><input ng-readonly="readOnly" ng-required="activeDistro.settings.port_range.max_port" name="minPort" type="number" ng-model="activeDistro.settings.port_range.min_port" class="form-control" placeholder="Min Port"></td> 114 <td><input ng-readonly="readOnly" ng-required="activeDistro.settings.port_range.min_port" name="maxPort" type="number" ng-model="activeDistro.settings.port_range.max_port" class="form-control" placeholder="Max Port"></td> 115 </tr> 116 </table> 117 <div class="icon fa fa-warning distro-error" ng-show="!checkPortRange(form.portRange.minPort.$modelValue, form.portRange.maxPort.$modelValue)">A non-negative, increasing port range is required</div> 118 </div> 119 <div> 120 <label class="distro-label">Cert.pem:</label> 121 <textarea ng-required="activeDistro.provider == 'docker'" name="cert" type="text" wrap="off" class="form-control" rows="5" ng-model="activeDistro.settings.auth.cert" style="margin-left: 0px;" placeholder="Paste your (PEM formatted) certificate here" ng-readonly="readOnly"></textarea> 122 <div class="icon fa fa-warning distro-error" ng-show="form.cert.$dirty && form.cert.$error.required || form.cert.$invalid">Valid certificate is required</div> 123 </div> 124 <div> 125 <label class="distro-label">Key.pem:</label> 126 <textarea ng-required="activeDistro.provider == 'docker'" name="key" type="text" wrap="off" class="form-control" rows="5" ng-model="activeDistro.settings.auth.key" style="margin-left: 0px;" placeholder="Paste your (PEM formatted) private key here" ng-readonly="readOnly"></textarea> 127 <div class="icon fa fa-warning distro-error" ng-show="form.key.$dirty && form.key.$error.required || form.key.$invalid">Valid key is required</div> 128 </div> 129 <div> 130 <label class="distro-label">Ca.pem:</label> 131 <textarea ng-required="activeDistro.provider == 'docker'" name="ca" type="text" wrap="off" class="form-control" rows="5" ng-model="activeDistro.settings.auth.ca" style="margin-left: 0px;" placeholder="Paste your (PEM formatted) certificate authority here" ng-readonly="readOnly"></textarea> 132 <div class="icon fa fa-warning distro-error" ng-show="form.ca.$dirty && form.ca.$error.required || form.ca.$invalid">Valid certificate authority is required</div> 133 </div> 134 </div> 135 <div ng-show="activeDistro.provider == 'digitalocean'"> 136 <div> 137 <label class="distro-label">Image ID:</label> 138 <input ng-readonly="readOnly" ng-required="activeDistro.provider == 'digitalocean'" name="imageID" type="number" class="form-control" ng-model="activeDistro.settings.image_id"> 139 <div class="icon fa fa-warning distro-error" ng-show="form.imageID.$dirty && form.imageID.$error.required || form.imageID.$invalid">Numeric image ID is required</div> 140 </div> 141 <div> 142 <label class="distro-label">Size ID:</label> 143 <input ng-readonly="readOnly" ng-required="activeDistro.provider == 'digitalocean'" name="sizeID" type="number" class="form-control" ng-model="activeDistro.settings.size_id"> 144 <div class="icon fa fa-warning distro-error" ng-show="form.sizeID.$dirty && form.sizeID.$error.required || form.sizeID.$invalid">Numeric size ID is required</div> 145 </div> 146 <div> 147 <label class="distro-label">Region ID:</label> 148 <input ng-readonly="readOnly" ng-required="activeDistro.provider == 'digitalocean'" name="regionID" type="number" class="form-control" ng-model="activeDistro.settings.region_id"> 149 <div class="icon fa fa-warning distro-error" ng-show="form.regionID.$dirty && form.regionID.$error.required || form.regionID.$invalid">Numeric region ID is required</div> 150 </div> 151 <div> 152 <label class="distro-label">SSH Key ID:</label> 153 <input ng-readonly="readOnly" ng-required="activeDistro.provider == 'digitalocean'" name="sshKeyID" type="number" class="form-control" ng-model="activeDistro.settings.ssh_key_id"> 154 <div class="icon fa fa-warning distro-error" ng-show="form.sshKeyID.$dirty && form.sshKeyID.$error.required || form.sshKeyID.$invalid">Numeric SSH Key ID is required</div> 155 </div> 156 </div> 157 <div ng-show="activeDistro.provider == 'ec2' || activeDistro.provider == 'ec2-spot'"> 158 <div> 159 <label class="distro-label">AMI ID:</label> 160 <input ng-readonly="readOnly" type="text" ng-required="activeDistro.provider == 'ec2' || activeDistro.provider == 'ec2-spot'" name="ami" class="form-control" ng-model="activeDistro.settings.ami" placeholder="EC2 image identifier e.g. ami-1ecae776" ng-readonly="readOnly"> 161 <div class="icon fa fa-warning distro-error" ng-show="form.ami.$dirty && form.ami.$error.required || form.ami.$invalid">AMI ID is required</div> 162 </div> 163 <div> 164 <label class="distro-label">Instance Type:</label> 165 <input ng-readonly="readOnly" type="text" ng-required="activeDistro.provider == 'ec2' || activeDistro.provider == 'ec2-spot'" name="instanceType" class="form-control" ng-model="activeDistro.settings.instance_type" placeholder="EC2 instance type for the AMI e.g t1.micro (must be available)" ng-readonly="readOnly"> 166 <div class="icon fa fa-warning distro-error" ng-show="form.instanceType.$dirty && form.instanceType.$error.required || form.instanceType.$invalid">Instance type is required</div> 167 </div> 168 <div ng-show="activeDistro.provider == 'ec2-spot'"> 169 <label class="distro-label">Bid Price:</label> 170 <input ng-readonly="readOnly" ng-required="activeDistro.provider == 'ec2-spot'" name="bidPrice" type="number" class="form-control" ng-model="activeDistro.settings.bid_price" placeholder="Maximum amount you're willing to pay per hour (dollars)"> 171 <div class="icon fa fa-warning distro-error" ng-show="form.bidPrice.$dirty && form.bidPrice.$error.required || form.bidPrice.$invalid">Numeric bid price is required</div> 172 </div> 173 <div> 174 <label class="distro-label">Key Name:</label> 175 <input type="text" ng-readonly="readOnly" ng-required="activeDistro.provider == 'ec2' || activeDistro.provider == 'ec2-spot'" name="keyName" class="form-control" ng-model="activeDistro.settings.key_name" placeholder="SSH Key (public part in EC2) to add on host machine" ng-readonly="readOnly"> 176 <div class="icon fa fa-warning distro-error" ng-show="form.keyName.$dirty && form.keyName.$error.required || form.keyName.$invalid">EC2 key name is required</div> 177 </div> 178 <div> 179 <label class="distro-label"><input style="margin-right:10px;" ng-disabled="readOnly" type="checkbox" name="is_vpc" ng-model="activeDistro.settings.is_vpc">Use security group in an EC2 VPC </label> <br> 180 <label class="distro-label">Security Group:</label> 181 <input type="text" ng-readonly="readOnly" ng-required="activeDistro.provider == 'ec2' || activeDistro.provider == 'ec2-spot'" name="securityGroup" ng-model="activeDistro.settings.security_group" placeholder="EC2 security group (must already exist)" class="form-control"> 182 <div class="icon fa fa-warning distro-error" ng-show="form.securityGroup.$dirty && form.securityGroup.$error.required || form.securityGroup.$invalid">Security group is required</div> 183 <div class="icon fa fa-warning distro-error" ng-show="!validSecurityGroup()">Security group for EC2 VPC must be the id (starts with 'sg-')</div> 184 </div> 185 <div ng-show="activeDistro.settings.is_vpc"> 186 <label class="distro-label"> Subnet Id</label> 187 <input type="text" name="subnet_id" ng-readonly="readOnly" class="form-control" ng-model="activeDistro.settings.subnet_id" placeholder="EC2 subnet id (must already exist) e.g subnet-xxxx" ng-required="activeDistro.settings.is_vpc"> 188 <div class="icon fa fa-warning distro-error" ng-show="form.securityGroup.$dirty && form.subnet_id.$error.required || form.subnet_id.$invalid || !validSubnetId()"> Subnet Id is required for EC2 VPC (must start with 'subnet-')</div> 189 </div> 190 <div ng-show="activeDistro.provider == 'ec2' || activeDistro.provider == 'ec2-spot'"> 191 <div id="mounts-table" class="distro-table-scroll"> 192 <label class="distro-label">Mount Points:</label> 193 <table ng-form name="mountPoints" class="table distro-table" ng-show="activeDistro.settings.mount_points" ng-init="form.devName=''; form.devSize=''"> 194 <thead class="muted"> 195 <tr> 196 <th>Device Name</th> 197 <th>Virtual Name</th> 198 <th>Size</th> 199 </tr> 200 </thead> 201 <tbody ng-repeat="mount_point in activeDistro.settings.mount_points"> 202 <tr> 203 <td><input ng-readonly="readOnly" required name="devName" type="text" ng-model="mount_point.device_name" class="form-control"></td> 204 <td><input ng-readonly="readOnly" ng-required="!mount_point.size" name="virtName" type="text" ng-model="mount_point.virtual_name" class="form-control"></td> 205 <td><input ng-readonly="readOnly" type="number" ng-required="!mount_point.virtual_name" name="devSize" type="text" ng-model="mount_point.size" class="form-control"></td> 206 <td ng-hide="readOnly"><a ng-click="form.$setDirty();removeMount(mount_point)"><i style="margin-top:9px" class="fa fa-trash distro-trash-icon"></i></a></td> 207 </tr> 208 </tbody> 209 </table> 210 </div> 211 </div> 212 <div> 213 <div class="icon fa fa-warning distro-error" ng-show="mountPoints.devName.$dirty && mountPoints.devName.$error.required">Device name is required<br /></div> 214 <div class="icon fa fa-warning distro-error" ng-show="mountPoints.devName.$dirty && mountPoints.virtName.$error.required && mountPoints.devSize.$error.required">Must specify either virtual device name or device size<br /></div> 215 <button ng-hide="readOnly" type="button" ng-disabled="mountPoints.devName.$dirty && mountPoints.$invalid || mountPoints.devName.$error.required" class="btn btn-primary" ng-click="form.$setDirty();addMount()"><i class="fa fa-plus"></i>Add Mount Point</button> 216 </div> 217 </div> 218 <div ng-show="activeDistro.provider != 'static'"> 219 <label class="distro-label">Maximum number of hosts allowed:</label> 220 <input ng-readonly="readOnly" type="number" ng-required="activeDistro.provider != 'static'" name="poolSize" class="form-control" ng-model="activeDistro.pool_size" placeholder="Max pool size e.g. 10"> 221 <div class="icon fa fa-warning distro-error" ng-show="form.poolSize.$dirty && form.poolSize.$error.required || form.poolSize.$invalid">Numeric pool size is required</div> 222 </div> 223 <div ng-form name="hostProviderForm" ng-show="activeDistro.provider == 'static'"> 224 <label class="distro-label">Hosts<span ng-show="activeDistro.settings.hosts && activeDistro.settings.hosts.length != 0">([[activeDistro.settings.hosts.length]])</span>:</label> 225 <div id="hosts-table" class="distro-table-scroll"> 226 <table style="margin-left: -8px;" class="table distro-table" ng-show="activeDistro.settings.hosts"> 227 <tbody id="hosts-table" > 228 <tr ng-repeat="host in activeDistro.settings.hosts"> 229 <td><input ng-readonly="readOnly" required name="hostName" type="text" ng-model="host.name" class="col-md-10" placeholder="Machine DNS name"></td> 230 <td ng-hide="readOnly"><a ng-click="form.$setDirty();removeHost(host)"><i class="fa fa-trash distro-trash-icon"></i></a></td> 231 </tr> 232 </tbody> 233 </table> 234 </div> 235 <div> 236 <div class="icon fa fa-warning distro-error" ng-show="hostProviderForm.hostName.$dirty && hostProviderForm.hostName.$error.required">Static host can not be blank<br /></div> 237 <br /> 238 <button type="button" ng-hide="readOnly" ng-disabled="hostProviderForm.hostName.$dirty && hostProviderForm.$invalid || hostProviderForm.hostName.$error.required" class="btn btn-primary" ng-click="form.$setDirty();addHost()"><i class="fa fa-plus"></i>Add Host</button> 239 </div> 240 </div> 241 </div> 242 <div> 243 <label class="distro-label">User:</label> 244 <input required ng-readonly="readOnly" name="userName" type="text" class="form-control" ng-model="activeDistro.user" placeholder="Username with which to SSH into host machine"> 245 <div class="icon fa fa-warning distro-error" ng-show="form.userName.$dirty && form.userName.$error.required || form.userName.$invalid">SSH user is required</div><br> 246 </div> 247 <div class="dropdown"> 248 <span class="distro-menu-title">SSH Key:</span> 249 <button class="btn btn-default dropdown-toggle" ng-disabled="readOnly" type="button" data-toggle="dropdown"> 250 <strong class="distro-menu-item">[[activeDistro.ssh_key]]<span class="fa fa-caret-down"></span></strong> 251 </button> 252 <ul class="dropdown-menu" role="menu" style="margin-left: 63px; align: left;"> 253 <li required name="sshKeyForm" ng-click="form.$setDirty();setKeyValue('ssh_key', key.name)" ng-repeat="key in keys" role="presentation"><a role="menuitem" tabindex="-1">[[key.name]] - [[key.location]]</a></li> 254 </ul> 255 <br> 256 <div class="icon fa fa-warning distro-error" ng-show="!activeDistro.ssh_key">SSH keys must be configured</div> 257 </div> 258 <div ng-form name="sshForm"> 259 <label class="distro-label">SSH Options:</label> 260 <div id="ssh-options-table" class="distro-table-scroll"> 261 <table style="margin-left: -8px;" class="table distro-table"> 262 <tbody ng-repeat="opt in activeDistro.ssh_options track by $index"> 263 <tr> 264 <td style="padding-left: 10px;"><input required ng-readonly="readOnly" name="opt" type="text" ng-model="activeDistro.ssh_options[$index]" 265 class="form-control" placeholder="e.g. BatchMode=yes"> 266 </td> 267 <td ng-hide="readOnly"> 268 <a ng-click="form.$setDirty();removeSSHOption(opt)"> 269 <i class="fa fa-trash distro-trash-icon"></i> 270 </a> 271 </td> 272 </tr> 273 </tbody> 274 </table> 275 </div> 276 <div class="icon fa fa-warning distro-error" ng-show="sshForm.opt.$dirty && sshForm.opt.$error.required">SSH option can not be blank<br /></div> 277 <button type="button" class="btn btn-primary" ng-hide="readOnly" ng-disabled="sshForm.opt.$dirty && sshForm.$invalid || sshForm.opt.$error.required" ng-click="form.$setDirty();addSSHOption()"><i class="fa fa-plus"></i>Add SSH Option</button> 278 </div> 279 <div> 280 <div> 281 <span style="float: right; margin-top: 20px;" class="distro-checkbox checkbox"><input ng-disabled="readOnly" type="checkbox" ng-model="activeDistro.setup_as_sudo">Run scripts as sudo</span> 282 <label class="distro-label">Setup Script:</label> 283 </div> 284 <textarea ng-readonly="readOnly" name="script" type="text" wrap="off" class="form-control" rows="7" ng-model="activeDistro.setup" style="margin-left: 0px; font-family: monospace"></textarea> 285 <div ng-hide="activeDistro.provider=='static'"> 286 <label class="distro-label">Teardown Script:</label> 287 <textarea ng-readonly="readOnly" name="script" type="text" wrap="off" class="form-control" rows="2" ng-model="activeDistro.teardown" style="margin-left: 0px; font-family: monospace"></textarea> 288 <div ng-show="activeDistro.teardown.length"><i label class="icon fa fa-warning warning-text"></i> 289 There is no guarantee this script will be run if the host is terminated by mechanisms outside of Evergreen. 290 </div> 291 </div> 292 </div> 293 <div> 294 <div ng-form name="expansions"> 295 <label class="distro-label">Expansions:</label> 296 <div id="expansions-table" class="distro-table-scroll"> 297 <table style="margin-left: -8px;" class="table distro-table" ng-show="activeDistro.expansions"> 298 <thead class="muted"> 299 <tr> 300 <th>Key</th> 301 <th>Value</th> 302 </tr> 303 </thead> 304 <tbody ng-repeat="expansion in activeDistro.expansions"> 305 <tr> 306 <td><input ng-readonly="readOnly" type="text" required name="expKey" ng-model="expansion.key" class="form-control"></td> 307 <td><input ng-readonly="readOnly" type="text" ng-model="expansion.value" class="form-control"></td> 308 <td ng-hide="readOnly"><a ng-click="form.$setDirty();removeExpansion(expansion)"><i class="fa fa-trash distro-trash-icon"></i></a></td> 309 </tr> 310 </tbody> 311 </table> 312 </div> 313 <div> 314 <div class="icon fa fa-warning distro-error" ng-show="expansions.expKey.$dirty && expansions.expKey.$error.required">Expansion key can not be blank<br /></div> 315 <button type="button" ng-hide="readOnly" ng-disabled="(expansions.expKey.$dirty && expansions.$invalid) || expansions.expKey.$error.required" class="btn btn-primary" ng-click="form.$setDirty();addExpansion()"><i class="fa fa-plus"></i>Add Expansion</button> 316 </div> 317 </div> 318 <div> 319 <p class="distro-checkbox checkbox"> 320 <input ng-disabled="readOnly" type="checkbox" ng-model="activeDistro.spawn_allowed"> 321 Allow users to spawn these hosts for personal use 322 </p> 323 </div> 324 </div> 325 </div> 326 <div ng-hide="readOnly"> 327 <br><br> 328 <p class="distro-checkbox checkbox" style="margin-left: 5px"> 329 <input ng-disabled="readOnly" type="checkbox" name="shouldDeco" ng-model="shouldDeco"> 330 Decommission hosts of this distro for this update 331 </p> 332 <button type="button" class="btn btn-primary" style="float: left; margin-left: 5px;" ng-disabled="form.$pristine || (form.$dirty && form.$invalid) || !validForm()" ng-click="saveConfiguration()">Save Configuration</button> 333 <button type="button" class="btn btn-danger" style="float: right; margin-right: 5px;" ng-click="openConfirmationModal('removeDistro')" ng-disabled="activeDistro.new">Remove Configuration</button> 334 <admin-modal> 335 <remove-distro ng-show="confirmationOption == 'removeDistro'"></remove-distro> 336 </admin-modal> 337 </div> 338 </div> 339 </div> 340 </div> 341 </div> 342 {{end}}