diff --git a/console-proxy/pom.xml b/console-proxy/pom.xml index 25265031258..0e12db0904f 100644 --- a/console-proxy/pom.xml +++ b/console-proxy/pom.xml @@ -25,6 +25,9 @@ cloudstack 4.1.0-SNAPSHOT + + mkisofs + log4j @@ -167,6 +170,19 @@ + + + genisoimage + + + /usr/bin/genisoimage + + + + genisoimage + + vmware @@ -209,7 +225,7 @@ - mkisofs + ${mkisofs} dist -quiet diff --git a/docs/en-US/offerings.xml b/docs/en-US/offerings.xml index 10252fd8af4..ca6312d7bfe 100644 --- a/docs/en-US/offerings.xml +++ b/docs/en-US/offerings.xml @@ -1,25 +1,22 @@ - %BOOK_ENTITIES; ]> - @@ -28,4 +25,5 @@ are discussed in the section on setting up networking for users. + diff --git a/docs/en-US/sys-offering-sysvm.xml b/docs/en-US/sys-offering-sysvm.xml new file mode 100644 index 00000000000..cccf3e04796 --- /dev/null +++ b/docs/en-US/sys-offering-sysvm.xml @@ -0,0 +1,75 @@ + + +%BOOK_ENTITIES; +]> + +
+ Changing the Default System Offering for System VMs + You can manually change the system offering for a particular System VM. Additionally, as a + &PRODUCT; administrator, you can also change the default system offering used for System + VMs. + + + Create a new system offering. + For more information, see + Creating a New System Service Offering. + + + Back up the database: + mysqldump -u root -p cloud | bzip2 > cloud_backup.sql.bz2 + + + Open an MySQL prompt: + mysql -u cloud -p cloud + + + Run the following queries on the cloud database. + + + In the disk_offering table, identify the original default offering and the new + offering you want to use by default. + Take a note of the ID of the new offering. + select id,name,unique_name,type from disk_offering; + + + For the original default offering, set the value of unique_name to NULL. + # update disk_offering set unique_name = NULL where id = 10; + Ensure that you use the correct value for the ID. + + + For the new offering that you want to use by default, set the value of unique_name + as follows: + For the default Console Proxy VM (CPVM) offering,set unique_name to + 'Cloud.com-ConsoleProxy'. For the default Secondary Storage VM (SSVM) offering, set + unique_name to 'Cloud.com-SecondaryStorage'. For example: + update disk_offering set unique_name = 'Cloud.com-ConsoleProxy' where id = 16; + + + + + Restart &PRODUCT; Management Server. Restarting is required because the default + offerings are loaded into the memory at startup. + service cloud-management restart + + + Destroy the existing CPVM or SSVM offerings and wait for them to be recreated. The new + CPVM or SSVM are configured with the new offering. + + +
diff --git a/setup/db/create-schema.sql b/setup/db/create-schema.sql index 2b724aa248b..338c5d916f9 100755 --- a/setup/db/create-schema.sql +++ b/setup/db/create-schema.sql @@ -1371,6 +1371,7 @@ CREATE TABLE `cloud`.`alert` ( `last_sent` DATETIME NULL COMMENT 'Last time the alert was sent', `resolved` DATETIME NULL COMMENT 'when the alert status was resolved (available memory no longer at critical level, etc.)', PRIMARY KEY (`id`), + INDEX `last_sent` (`last_sent` DESC), CONSTRAINT `uc_alert__uuid` UNIQUE (`uuid`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; diff --git a/setup/db/db/schema-40to410.sql b/setup/db/db/schema-40to410.sql index 93949b8e4fa..ed4946e54dd 100644 --- a/setup/db/db/schema-40to410.sql +++ b/setup/db/db/schema-40to410.sql @@ -77,6 +77,8 @@ ALTER TABLE `cloud`.`inline_load_balancer_nic_map` DROP COLUMN load_balancer_id; ALTER TABLE upload ADD uuid VARCHAR(40); ALTER TABLE async_job modify job_cmd VARCHAR(255); +ALTER TABLE `cloud`.`alert` ADD INDEX `last_sent` (`last_sent` DESC) ; + -- populate uuid column with db id if uuid is null UPDATE `cloud`.`account` set uuid=id WHERE uuid is NULL; UPDATE `cloud`.`alert` set uuid=id WHERE uuid is NULL; diff --git a/tools/cli/cloudmonkey/cachegen.py b/tools/cli/cloudmonkey/cachegen.py index e03b6fddf89..509c0c68587 100644 --- a/tools/cli/cloudmonkey/cachegen.py +++ b/tools/cli/cloudmonkey/cachegen.py @@ -16,11 +16,13 @@ # under the License. try: - from common import grammar + import re from marvin.cloudstackAPI import * from marvin import cloudstackAPI except ImportError, e: - pass + import sys + print "ImportError", e + sys.exit(1) completions = cloudstackAPI.__all__ @@ -43,9 +45,12 @@ def main(): completing commands and help docs. This reduces the overall search and cache_miss (computation) complexity from O(n) to O(1) for any valid cmd. """ + pattern = re.compile("[A-Z]") + verbs = list(set([x[:pattern.search(x).start()] for x in completions + if pattern.search(x) is not None]).difference(['cloudstack'])) # datastructure {'verb': {cmd': ['api', [params], doc, required=[]]}} cache_verbs = {} - for verb in grammar: + for verb in verbs: completions_found = filter(lambda x: x.startswith(verb), completions) cache_verbs[verb] = {} for api_name in completions_found: diff --git a/tools/cli/cloudmonkey/cloudmonkey.py b/tools/cli/cloudmonkey/cloudmonkey.py index eadf23ff173..ecd0c82fb11 100644 --- a/tools/cli/cloudmonkey/cloudmonkey.py +++ b/tools/cli/cloudmonkey/cloudmonkey.py @@ -26,6 +26,7 @@ try: import logging import os import pdb + import re import shlex import sys import time @@ -38,7 +39,7 @@ try: from prettytable import PrettyTable from common import __version__, config_file, config_fields - from common import grammar, precached_verbs + from common import precached_verbs from marvin.cloudstackConnection import cloudConnection from marvin.cloudstackException import cloudstackAPIException from marvin.cloudstackAPI import * @@ -71,12 +72,12 @@ class CloudMonkeyShell(cmd.Cmd, object): ruler = "=" config_file = config_file config_fields = config_fields - grammar = grammar # datastructure {'verb': {cmd': ['api', [params], doc, required=[]]}} cache_verbs = precached_verbs - def __init__(self, pname): + def __init__(self, pname, verbs): self.program_name = pname + self.verbs = verbs if os.path.exists(self.config_file): config = self.read_config() else: @@ -102,11 +103,9 @@ class CloudMonkeyShell(cmd.Cmd, object): logger.debug("Loaded config fields:\n%s" % self.config_fields) cmd.Cmd.__init__(self) - # Update config if config_file does not exist if not os.path.exists(self.config_file): config = self.write_config() - # Enable history support try: if os.path.exists(self.history_file): readline.read_history_file(self.history_file) @@ -381,13 +380,13 @@ class CloudMonkeyShell(cmd.Cmd, object): def completedefault(self, text, line, begidx, endidx): partitions = line.partition(" ") - verb = partitions[0] - rline = partitions[2].partition(" ") + verb = partitions[0].strip() + rline = partitions[2].lstrip().partition(" ") subject = rline[0] separator = rline[1] - params = rline[2] + params = rline[2].lstrip() - if verb not in self.grammar: + if verb not in self.verbs: return [] autocompletions = [] @@ -436,7 +435,7 @@ class CloudMonkeyShell(cmd.Cmd, object): args = args.strip().partition(" ") key, value = (args[0], args[2]) setattr(self, key, value) # keys and attributes should have same names - self.prompt = self.prompt.strip() + " " # prompt fix + self.prompt = self.prompt.strip() + " " # prompt fix self.write_config() def complete_set(self, text, line, begidx, endidx): @@ -513,22 +512,22 @@ class CloudMonkeyShell(cmd.Cmd, object): def main(): - # Create handlers on the fly using closures - self = CloudMonkeyShell - global grammar - for rule in grammar: - def add_grammar(rule): + pattern = re.compile("[A-Z]") + verbs = list(set([x[:pattern.search(x).start()] for x in completions + if pattern.search(x) is not None]).difference(['cloudstack'])) + for verb in verbs: + def add_grammar(verb): def grammar_closure(self, args): - if self.pipe_runner("%s %s" % (rule, args)): + if self.pipe_runner("%s %s" % (verb, args)): return try: args_partition = args.partition(" ") - res = self.cache_verbs[rule][args_partition[0]] + res = self.cache_verbs[verb][args_partition[0]] cmd = res[0] helpdoc = res[2] args = args_partition[2] except KeyError, e: - self.print_shell("Error: invalid %s api arg" % rule, e) + self.print_shell("Error: invalid %s api arg" % verb, e) return if ' --help' in args or ' -h' in args: self.print_shell(helpdoc) @@ -536,12 +535,12 @@ def main(): self.default("%s %s" % (cmd, args)) return grammar_closure - grammar_handler = add_grammar(rule) - grammar_handler.__doc__ = "%ss resources" % rule.capitalize() - grammar_handler.__name__ = 'do_' + rule - setattr(self, grammar_handler.__name__, grammar_handler) + grammar_handler = add_grammar(verb) + grammar_handler.__doc__ = "%ss resources" % verb.capitalize() + grammar_handler.__name__ = 'do_' + verb + setattr(CloudMonkeyShell, grammar_handler.__name__, grammar_handler) - shell = CloudMonkeyShell(sys.argv[0]) + shell = CloudMonkeyShell(sys.argv[0], verbs) if len(sys.argv) > 1: shell.onecmd(' '.join(sys.argv[1:])) else: diff --git a/tools/cli/cloudmonkey/common.py b/tools/cli/cloudmonkey/common.py index 0865a8ee8cc..5adb6d4a576 100644 --- a/tools/cli/cloudmonkey/common.py +++ b/tools/cli/cloudmonkey/common.py @@ -39,12 +39,3 @@ config_fields = {'host': 'localhost', 'port': '8080', 'history_file': os.path.expanduser('~/.cloudmonkey_history')} -# Add verbs in grammar -grammar = ['create', 'list', 'delete', 'update', 'lock', - 'enable', 'activate', 'disable', 'add', 'remove', - 'attach', 'detach', 'associate', 'disassociate', 'generate', 'ldap', - 'assign', 'authorize', 'change', 'register', 'configure', - 'start', 'restart', 'reboot', 'stop', 'reconnect', - 'cancel', 'destroy', 'revoke', 'mark', 'reset', - 'copy', 'extract', 'migrate', 'restore', 'suspend', - 'get', 'query', 'prepare', 'deploy', 'upload'] diff --git a/ui/scripts/accounts.js b/ui/scripts/accounts.js index 59e9d8bbd15..4c327ee759b 100644 --- a/ui/scripts/accounts.js +++ b/ui/scripts/accounts.js @@ -642,12 +642,16 @@ }, vmLimit: { label: 'label.instance.limits', - isEditable: function(context) { + isEditable: function(context) { + + if(context.accounts == undefined) + return false; + else { if (context.accounts[0].accounttype == roleTypeUser || context.accounts[0].accounttype == roleTypeDomainAdmin) //updateResourceLimits is only allowed on account whose type is user or domain-admin return true; else return false; - } + } } }, ipLimit: { label: 'label.ip.limits', diff --git a/ui/scripts/system.js b/ui/scripts/system.js index bd8d3316103..def6d443026 100644 --- a/ui/scripts/system.js +++ b/ui/scripts/system.js @@ -1322,6 +1322,13 @@ networkOfferingObjs = json.listnetworkofferingsresponse.networkoffering; if (networkOfferingObjs != null && networkOfferingObjs.length > 0) { for (var i = 0; i < networkOfferingObjs.length; i++) { + + if(args.scope=="account-specific" && args.context.zones[0].securitygroupsenabled == true) { //BUG - CLOUDSTACK-1063 + var serviceObjArray = networkOfferingObjs[i].name; + if(serviceObjArray == "DefaultSharedNetworkOfferingWithSGService"){ + continue; + } + } //comment out the following 12 lines because of CS-16718 /* diff --git a/ui/scripts/ui-custom/instanceWizard.js b/ui/scripts/ui-custom/instanceWizard.js index 67d2a69c968..6e993843562 100644 --- a/ui/scripts/ui-custom/instanceWizard.js +++ b/ui/scripts/ui-custom/instanceWizard.js @@ -860,8 +860,19 @@ // Previous button if ($target.closest('div.button.previous').size()) { - var index = $steps.filter(':visible').index(); - if (index) showStep(index); + var $step = $steps.filter(':visible'); + var $networkStep = $steps.filter('.network'); + var index = $step.index(); + + $networkStep.removeClass('next-use-security-groups'); + + if (index) { + if (index == $steps.size() - 1 && $networkStep.hasClass('next-use-security-groups')) { + showStep(5); + } else { + showStep(index); + } + } return false; } diff --git a/ui/scripts/ui/dialog.js b/ui/scripts/ui/dialog.js index 5236bb6a334..88dba3fa498 100644 --- a/ui/scripts/ui/dialog.js +++ b/ui/scripts/ui/dialog.js @@ -57,22 +57,80 @@ var fields = $.map(args.form.fields, function(value, key) { return key; }) - - $(fields).each(function() { + + var ret = function() { + return $formContainer.dialog({ + dialogClass: 'create-form', + closeOnEscape: false, + draggable: false, + width: 400, + title: _l(args.form.title), + open: function() { + if (args.form.preFilter) { + args.form.preFilter({ $form: $form, context: args.context }); + } + }, + buttons: [ + { + text: createLabel ? createLabel : _l('label.ok'), + 'class': 'ok', + click: function() { + if (!complete($formContainer)) { return false; } + + $('div.overlay').remove(); + $('.tooltip-box').remove(); + $formContainer.remove(); + $(this).dialog('destroy'); + + $('.hovered-elem').hide(); + + return true; + } + }, + { + text: _l('label.cancel'), + 'class': 'cancel', + click: function() { + $('div.overlay').remove(); + $('.tooltip-box').remove(); + $formContainer.remove(); + $(this).dialog('destroy'); + + $('.hovered-elem').hide(); + } + } + ] + }).closest('.ui-dialog').overlay(); + }; + + var isLastAsync = function(idx) { + for(var i = idx+1; i < $(fields).length ; i++) { + var f = args.form.fields[$(fields).get(i)]; + if(f.select || f.dynamic){ + return false; + } + } + return true; + }; + + var isAsync = false; + var isNoDialog = args.noDialog ? args.noDialog : false; + + $(fields).each(function(idx, element) { var key = this; var field = args.form.fields[key]; var $formItem = $('
') .addClass('form-item') .attr({ rel: key }); - + if(field.isHidden != null) { - if (typeof(field.isHidden) == 'boolean' && field.isHidden == true) - $formItem.hide(); - else if (typeof(field.isHidden) == 'function' && field.isHidden() == true) - $formItem.hide(); + if (typeof(field.isHidden) == 'boolean' && field.isHidden == true) + $formItem.hide(); + else if (typeof(field.isHidden) == 'function' && field.isHidden() == true) + $formItem.hide(); } - + $formItem.appendTo($form); //Handling Escape KeyPress events @@ -96,7 +154,7 @@ closeOnEscape: false }); */ // Label field - + var $name = $('
').addClass('name') .appendTo($formItem) .append( @@ -104,9 +162,10 @@ ); // red asterisk - var $astersikSpan = $('').addClass('field-required').html('*'); - $name.find('label').prepend($astersikSpan); - if (field.validation == null || field.validation.required == false) { + var $astersikSpan = $('').addClass('field-required').html('*'); + $name.find('label').prepend($astersikSpan); + + if (field.validation == null || field.validation.required == false) { $astersikSpan.hide(); } @@ -169,6 +228,7 @@ // Determine field type of input if (field.select) { + isAsync = true; selectArgs = { context: args.context, response: { @@ -193,6 +253,10 @@ } $input.trigger('change'); + + if((!isNoDialog) && isLastAsync(idx)) { + ret(); + } } } }; @@ -200,7 +264,7 @@ selectFn = field.select; $input = $('').attr({ - name: key, - type: 'text' - }).appendTo($value); + } else if (field.isDatepicker) { //jQuery datepicker + $input = $('').attr({ + name: key, + type: 'text' + }).appendTo($value); - if (field.defaultValue) { - $input.val(field.defaultValue); - } - if (field.id) { - $input.attr('id', field.id); - } - $input.addClass("disallowSpecialCharacters"); - $input.datepicker({dateFormat: 'yy-mm-dd'}); - - } else if(field.range) { //2 text fields on the same line (e.g. port range: startPort - endPort) - $input = $.merge( - // Range start - $('').attr({ - type: 'text', - name: field.range[0] - }), + if (field.defaultValue) { + $input.val(field.defaultValue); + } + if (field.id) { + $input.attr('id', field.id); + } + $input.addClass("disallowSpecialCharacters"); + $input.datepicker({dateFormat: 'yy-mm-dd'}); - // Range end - $('').attr({ - type: 'text', - name: field.range[1] - }) - ).appendTo( - $('
').addClass('range-edit').appendTo($value) - ); - $input.wrap($('
').addClass('range-item')); - $input.addClass("disallowSpecialCharacters"); - - } else { //text field - $input = $('').attr({ - name: key, - type: field.password || field.isPassword ? 'password' : 'text' - }).appendTo($value); + } else if(field.range) {//2 text fields on the same line (e.g. port range: startPort - endPort) + $input = $.merge( + // Range start + $('').attr({ + type: 'text', + name: field.range[0] + }), - if (field.defaultValue) { - $input.val(field.defaultValue); - } - if (field.id) { - $input.attr('id', field.id); - } + // Range end + $('').attr({ + type: 'text', + name: field.range[1] + }) + ).appendTo( + $('
').addClass('range-edit').appendTo($value) + ); + $input.wrap($('
').addClass('range-item')); + $input.addClass("disallowSpecialCharacters"); + + } else { //text field + $input = $('').attr({ + name: key, + type: field.password || field.isPassword ? 'password' : 'text' + }).appendTo($value); + + if (field.defaultValue) { + $input.val(field.defaultValue); + } + if (field.id) { + $input.attr('id', field.id); + } $input.addClass("disallowSpecialCharacters"); } - if(field.validation != null) + if(field.validation != null) $input.data('validation-rules', field.validation); - else + else $input.data('validation-rules', {}); var fieldLabel = field.label; + var inputId = $input.attr('id') ? $input.attr('id') : fieldLabel.replace(/\./g,'_'); - + $input.attr('id', inputId); $name.find('label').attr('for', inputId); @@ -380,29 +449,32 @@ attachTo: '.form-item' }); } + + /* $input.blur(function() { console.log('tooltip remove->' + $input.attr('name')); });*/ }); - + + var getFormValues = function() { var formValues = {}; $.each(args.form.fields, function(key) {}); }; - // Setup form validation + // Setup form validation $formContainer.find('form').validate(); - $formContainer.find('input, select').each(function() { + $formContainer.find('input, select').each(function() { if ($(this).data('validation-rules')) { $(this).rules('add', $(this).data('validation-rules')); } - else { - $(this).rules('add', {}); - } - }); - $form.find('select').trigger('change'); - - + else { + $(this).rules('add', {}); + } + }); + $form.find('select').trigger('change'); + + var complete = function($formContainer) { var $form = $formContainer.find('form'); var data = cloudStack.serializeForm($form); @@ -429,93 +501,48 @@ $formContainer: $formContainer, completeAction: complete }; + } else if (!isAsync) { + return ret(); } - - return $formContainer.dialog({ - dialogClass: 'create-form', - closeOnEscape: false, - draggable: false, - width: 400, - title: _l(args.form.title), - open: function() { - if (args.form.preFilter) { - args.form.preFilter({ $form: $form, context: args.context }); - } - }, - buttons: [ - { - text: createLabel ? createLabel : _l('label.ok'), - 'class': 'ok', - click: function() { - if (!complete($formContainer)) { return false; } - - $('div.overlay').remove(); - $('.tooltip-box').remove(); - $formContainer.remove(); - $(this).dialog('destroy'); - - $('.hovered-elem').hide(); - - return true; - } - }, - { - text: _l('label.cancel'), - 'class': 'cancel', - click: function() { - $('div.overlay').remove(); - $('.tooltip-box').remove(); - $formContainer.remove(); - $(this).dialog('destroy'); - - $('.hovered-elem').hide(); - } - } - ] - }).closest('.ui-dialog').overlay(); }, - /** + /** * to change a property(e.g. validation) of a createForm field after a createForm is rendered */ - createFormField: { - validation: { - required: { - add: function($formField) { - var $input = $formField.find('input, select'); - var validationRules = $input.data('validation-rules'); - - if(validationRules == null || validationRules.required == null || validationRules.required == false) { - $formField.find('.name').find('label').find('span.field-required').css('display', 'inline'); //show red asterisk - - if(validationRules == null) - validationRules = {}; - validationRules.required = true; - $input.data('validation-rules', validationRules); - - $input.rules('add', { required: true }); - } - - }, - remove: function($formField) { + createFormField: { + validation: { + required: { + add: function($formField) { var $input = $formField.find('input, select'); - var validationRules = $input.data('validation-rules'); - - if(validationRules != null && validationRules.required != null && validationRules.required == true) { - $formField.find('.name').find('label').find('span.field-required').hide(); //hide red asterisk - - delete validationRules.required; - $input.data('validation-rules', validationRules); + var validationRules = $input.data('validation-rules'); + + if(validationRules == null || validationRules.required == null || validationRules.required == false) { + $formField.find('.name').find('label').find('span.field-required').css('display', 'inline'); //show red asterisk + + if(validationRules == null) + validationRules = {}; + validationRules.required = true; + $input.data('validation-rules', validationRules); + $input.rules('add', { required: true }); + } + }, + remove: function($formField) { + var $input = $formField.find('input, select'); + var validationRules = $input.data('validation-rules'); + + if(validationRules != null && validationRules.required != null && validationRules.required == true) { + $formField.find('.name').find('label').find('span.field-required').hide(); //hide red asterisk + delete validationRules.required; + $input.data('validation-rules', validationRules); + + $input.rules('remove', 'required'); + $formField.find('.value').find('label.error').hide(); + } + } + } + } + }, - $input.rules('remove', 'required'); - - $formField.find('.value').find('label.error').hide(); - } - } - } - } - }, - /** * Confirmation dialog */