Error when using the SoftLayer Ruby API to specify a flavor - ibm-cloud

I have a working ruby script that we have been using for a quite a while to order VSIs from SoftLayer. The script specifies a certain price item for CPU, one for memory, and another for disk. I am trying to modify the script to work with flavors but I have been unable to figure out what I am doing wrong. Basically I have removed the CPU, memory, and disk price items from the product order and added in a flavorKeyName in the supplementalCreateObjectOptions like this:
#!/usr/bin/ruby
require 'softlayer_api'
client = SoftLayer::Client.new(username: 'XXXXX', api_key: 'XXXXX')
productOrder = {
'virtualGuests' => [{
'hostname' => 'test',
'domain' => 'mycompany.com',
'primaryNetworkComponent' => { 'networkVlan' => { 'id' => XXXXXX } },
'primaryBackendNetworkComponent' => { 'networkVlan' => { 'id' => XXXXXX },
'supplementalCreateObjectOptions' => { 'flavorKeyName' => 'B1_1X2X100' } }
}],
'location' => XXXXXX,
'packageId' => 46,
'imageTemplateId' => XXXXX,
'useHourlyPricing' => true,
'prices' => [
{'id' => 34183 }, # 0 GB Bandwidth
{'id' => 24713 }, # 1 Gbps Public & Private Network Uplinks
{'id' => 34807 }, # 1 IP Address
{'id' => 33483 }, # Unlimited SSL VPN Users & 1 PPTP VPN User per account
{'id' => 34241 }, # Host Ping and TCP Service Monitoring
{'id' => 32500 }, # Email and Ticket
{'id' => 35310 }, # NESSUS_VULNERABILITY_ASSESSMENT_REPORTING
{'id' => 23070 }, # REBOOT_REMOTE_CONSOLE
{'id' => 32627 } # AUTOMATED_NOTIFICATION
]
}
order = client['Product_Order'].verifyOrder(productOrder)
but this fails with:
/usr/lib64/ruby/2.1.0/xmlrpc/client.rb:271:in `call': Internal Error (XMLRPC::FaultException)
from /usr/lib64/ruby/gems/2.1.0/gems/softlayer_api-3.2.2/lib/softlayer/Service.rb:269:in `call_softlayer_api_with_params'
from /usr/lib64/ruby/gems/2.1.0/gems/softlayer_api-3.2.2/lib/softlayer/Service.rb:198:in `method_missing'
from /tmp/yy2:34:in `<main>'
The error is not too helpful on what I might be specifying incorrectly or might be missing.
Does any one have a suggestions on what I might be doing wrong?

When using Softlayer_Product_Order::verifyOrder or Softlayer_Product_Order::placeOrder you need to use the package 835, and set the presetId parameter to specify what flavor configuration you want to order.
The supplementalCreateObjectOptions parameter is specified when using the SoftLayer_Virtual_Guest::createObject method.
Following are two ways to order virtual guest devices with a flavor configuration.
PlaceOrder
To get the list of available preset ids for package 835 you need to use the method SoftLayer_Product_Package::getActivePresets.
https://api.softlayer.com/rest/v3/SoftLayer_Product_Package/835/getActivePresets
Check the keyName values to know which are Balanced, Memory, etc., they should start with:
B1 is for "Balanced"
BL1 is for "Balanced Local Storage"
BL2 is for "Balanced Local Storage - SSD"
C1 is for "Compute"
M1 is for "Memory"
These characters are followed by a short description of VSI configuration as following:
C1_2X2X100 for Compute VSI with "2 x 2.0 GHz Cores, 2 GB RAM, 100 GB Disk"
B1_8X16X25 for Balanced VSI with "8 x 2.0 GHz Cores, 16 GB RAM, 25 GB Disk"
If I'm not wrong the presetId 333 is for B1_1X2X100 which is the flavor configuration you want.
require 'rubygems'
require 'softlayer_api'
require 'json'
# Your SoftLayer API username and API Key.
USERNAME = 'set-me'
API_KEY = 'set-me'
# Location where server will be provisioned.
location = 'AMSTERDAM03'
# The id of the SoftLayer_Product_Package, use the 835 for VSI Families.
package_id = 835
# Following is the preset id used to complete this example.
preset_id = 333 # B1_1X2X100 (1 x 2.0 GHz Cores, 2 GB RAM, and primary disk of 25 GB)
# The number of servers you wish to order in this configuration.
quantity = 1
# Build a skeleton SoftLayer_Virtual_Guest object. If you set quantity greater than 1
# then you need to define one hostname/domain per server you wish to order.
virtual_guest = [
{
'hostname' => 'test-vsi',
'domain' => 'mycompany.com',
'primaryNetworkComponent' => { 'networkVlan' => { 'id' => 11111 } },
'primaryBackendNetworkComponent' => { 'networkVlan' => { 'id' => 22222 }}
}
]
# Specify the item prices. Note that you don't need to specify the item price for
# cpus, ram, and primary disk, and take into account that “Balanced Local Storage”
# and “Balanced Local Storage - SSD” requires a second disk, the system will select one
# if you don’t specify it.
prices = [
{'id' => 34183 }, # 0 GB Bandwidth
{'id' => 24713 }, # 1 Gbps Public & Private Network Uplinks
{'id' => 34807 }, # 1 IP Address
{'id' => 33483 }, # Unlimited SSL VPN Users & 1 PPTP VPN User per account
{'id' => 34241 }, # Host Ping and TCP Service Monitoring
{'id' => 32500 }, # Email and Ticket
{'id' => 35310 }, # NESSUS_VULNERABILITY_ASSESSMENT_REPORTING
{'id' => 23070 }, # REBOOT_REMOTE_CONSOLE
{'id' => 32627 } # AUTOMATED_NOTIFICATION
]
# Build a skeleton SoftLayer_Container_Product_Order object containing the order
# you wish to place.
order_template = {
'quantity' => quantity,
'location' => location,
'packageId' => package_id,
'presetId' => preset_id,
'imageTemplateId' => 1111111,
'useHourlyPricing' => true,
'prices' => prices,
'virtual_guest' => virtual_guest
}
# Declare the API client to use the SoftLayer_Product_Order API service
client = SoftLayer::Client.new(username: USERNAME, api_key: API_KEY)
product_order_service = client.service_named('SoftLayer_Product_Order')
begin
# verifyOrder() will check your order for errors. Replace this with placeOrder()
# when you're ready to order.
receipt = product_order_service.verifyOrder(order_template)
puts JSON.pretty_generate(receipt)
rescue StandardError => exception
puts "There was an error in your order: #{exception}"
end
CreateObject
Take account that createObject method is a simplified way to order virtual guest devices so you may not be able to set items like IPV6, secondary IP address, etc. See SoftLayer_Virtual_Guest::createObject to know which properties you can set.
The following example is to order a vsi family with flavor configuration B1_1X2X100, on this case it is necessary to set the parameter supplementalCreateObjectOptions
require 'rubygems'
require 'softlayer_api'
require 'json'
# Your SoftLayer API username and API Key.
USERNAME = 'set-me'
API_KEY = 'set-me'
# Build the skeleton of SoftLayer_Virtual_Guest object.
virtual_guest_template = {
'hostname' => 'test-vsi',
'domain' => 'mycompany.com',
'primaryNetworkComponent' => { 'networkVlan' => { 'id' => 11111 } },
'primaryBackendNetworkComponent' => { 'networkVlan' => { 'id' => 22222 }},
'datacenter' => { 'name' => 'dal05' },
'supplementalCreateObjectOptions' => {
'flavorKeyName' => 'B1_1X2X100'
},
'hourlyBillingFlag' => true,
# Following is to specify the imageTemplate you want to use. But on this case you need
# to set the globalIdentifier of imageTemplate.
'blockDeviceTemplateGroup' => {
'globalIdentifier' => '6x06c3x8-4158-4b69-ba5x-433c18x3xac3'
},
'networkComponents' => [
{ 'maxSpeed' => 1000} # 1 Gbps Public & Private Network Uplinks
]
}
# Declare the API client to use the SoftLayer_Virtual_Guest API service
client = SoftLayer::Client.new(username: USERNAME, api_key: API_KEY)
virtual_guest_service = client['SoftLayer_Virtual_Guest']
begin
# Call to createObject() when you're ready to order.
# Call to generateOrderTemplate() if you want to create an order container that can be
# used with the methods verifyOrder and placeOrder.
virtual_guest = virtual_guest_service.createObject(virtual_guest_template)
puts JSON.pretty_generate(virtual_guest)
rescue StandardError => exception
puts "There was an error in your order: #{exception}"
end

Related

PE Puppet Console and manage multiple node user accounts

I'm new to puppet and using Puppet Enterprise with the module from puppet for accounts. https://forge.puppet.com/puppetlabs/accounts
I'd like to be able to manage the user account details from the PE console for multiple users.
The best I can seem to get to is managing them within
/etc/puppetlabs/code/environments/production/manifests/site.pp
Which contains:
node default {
accounts::user { 'jeff':
comment => 'Jeff McCune',
groups => [
'wheel',
],
locked => true,
sshkeys => [
'ssh-rsa AAAA...',
'ssh-dss AAAA...',
],
password => '!!',
ensure => 'present',
}
accounts::user { 'dave':
comment => 'Dave Smith',
groups => [
'wheel',
],
locked => true,
sshkeys => [
'ssh-rsa AAAA...',
'ssh-dss AAAA...',
],
password => '!!',
ensure => 'present',
}
}
I'd ideally I'd like to be able to manage them per PE classification within the console. So each users details can be parameters I enter in the PE console - the number of users would vary, but just need the principle to able to add more than one.
I've tried adding into a manifest but struggled with declaring more than one account. I did that by:
/etc/puppetlabs/code/environments/production/modules/my_app/manifests/init.pp
which contained:
class my_app (
$username => '',
$usercomment => '',
$sshkey ='',
){
accounts::user { $username:
comment => $usercomment,
groups => [
'sudonopw',
],
locked => false,
sshkeys => [
$sshkey,
],
password => '!!',
ensure => present,
}
}
This allowed me to apply this class to any variation of my node classification and allow me to manage those parameters in the PE console. However I couldn't add more than one account to each node, as the class had been declared.
Can anyone give me some pointers.
Thanks

Puppet : get mountpoint using facter filter

I am trying to get a root partition (mountpoint => "/") name using Puppet facter. When I run "facter mountpoints", it shows multiple partitions. I would like to get the variable "/dev/md3" from the result.
{
/ => {
available => "893.71 GiB",
available_bytes => 959608590336,
capacity => "1.86%",
device => "/dev/md3",
filesystem => "ext4",
options => [
"rw",
"errors=remount-ro"
],
size => "910.69 GiB",
size_bytes => 977843884032,
used => "16.98 GiB",
used_bytes => 18235293696
},
/run => {
available => "794.91 MiB",
available_bytes => 833527808,
capacity => "0.07%",
device => "tmpfs",
filesystem => "tmpfs",
options => [
"rw",
"noexec",
"nosuid",
"size=10%",
"mode=0755"
],
size => "795.48 MiB",
size_bytes => 834125824,
used => "584.00 KiB",
used_bytes => 598016
},
/tmp => {
available => "1.78 GiB",
available_bytes => 1909157888,
capacity => "1.21%",
device => "/dev/md1",
filesystem => "ext4",
options => [
"rw"
],
size => "1.80 GiB",
size_bytes => 1932533760,
used => "22.29 MiB",
used_bytes => 23375872
}
}
I tried to use filter, but I was not able to filter "/" device.
$root_mount = $facts['mountpoints'].filter |$mountpoint| { $mountpoint == '/' } Do you guys have any idea?
You can access this fact directly via hash notation. Since your question heavily implies you are using Facter 3/Puppet 4, I will work with that syntax.
You just directly traverse the keys in the Facter hash to arrive at the /dev/md3 value. If we minimize the hash to the relevant portion from facter mountpoints:
{
/ => {
device => "/dev/md3"
}
}
then we see that the keys are mountpoints (you accessed that key directly when you did facter mountpoints from the CLI), /, and device. Therefore, using standard hash notation in Puppet with the $facts hash, we can access that value with:
$facts['mountpoints']['/']['device'] # /dev/md3
Check here for more info: https://docs.puppet.com/puppet/4.9/lang_facts_and_builtin_vars.html#the-factsfactname-hash

Perl using USAePay billing module

I was wondering if anybody has experience using the USAePay billing module? I have reached out to the developer and his response was he does not have time to help people. My problem is this -
I have written the following code to add a customer's billing information using the Sandbox server but it looks as though the module defaults to the production server so each time I attempt to validate the transaction I get the response -
Card was rejected: Specified source key not found.
How do I tell it to use the Sandbox server?
use strict;
use warnings;
use Business::OnlinePayment;
use constant {
LOGIN => 'source key', #USAePay source key
PASSWORD => '12345', #USAePay PIN
};
my $tx = new Business::OnlinePayment("USAePay");
$tx->content(
login => LOGIN,
password => PASSWORD,
type => 'CC',
action => 'Recurring Authorization',
description => 'Business::OnlinePayment test',
amount => '49.95',
invoice_number => '100100',
name => 'Tofu Beast',
card_number => '4000100011112224',
expiration => '09/19',
address => '1234 Bean Curd Lane',
city => 'San Francisco',
state => 'CA',
zip => '94102',
);
$tx->submit();
if($tx->is_success()) {
print 'Card processed successfully: '.$tx->authorization.'\n';
} else {
print 'Card was rejected: '.$tx->error_message."\n";
}
As Business::OnlinePayment::USAePay is a processor for Business::OnlinePayment, but doesn't have a lot of docs itself, a look at the docs of Business::OnlinePayment might help. It reveales the test_transaction method.
Most processors provide a test mode, where submitted transactions will not actually be charged or added to your batch, calling this function with a true argument will turn that mode on if the processor supports it, or generate a fatal error if the processor does not support a test mode (which is probably better than accidentally making real charges).
An untested example:
my $tx = new Business::OnlinePayment("USAePay");
$tx->test_transaction; # here
$tx->content(
login => LOGIN,
password => PASSWORD,
type => 'CC',
action => 'Recurring Authorization',
description => 'Business::OnlinePayment test',
amount => '49.95',
invoice_number => '100100',
name => 'Tofu Beast',
card_number => '4000100011112224',
expiration => '09/19',
address => '1234 Bean Curd Lane',
city => 'San Francisco',
state => 'CA',
zip => '94102',
);
$tx->submit();
Digging a bit deeper in the source of Business::OnlinePayment::USAePay shows that this particular processor actually has three different test modes.
# test_transaction(0): normal mode
# 1 : test mode (validates formatting only)
# 2 : use sandbox server
# 3 : test mode on sandbox server
It looks like you can set the server details in the constructor - it takes an optional hash of parameters beyond the processor name.
Have you tried something like:
my $tx = new Business::OnlinePayment(
"USAePay",
Server => 'https://sandbox.usaepay.com/gate'
);

vagrant postgresql and external db gui

I'm using PuPHPet/Puppet/Vagrant to set up a VM with nginx and postgresql. I'd like to be able to connect to the postgresql database with a GUI. I don't know the required steps to set this up though.
I think i need to forward port 5432 to 5432 on my local machine and then edit the pg_hba.conf to allow for outside connections but i don't know what that needs to look like.
Here's my current Vagrantfile(doesn't have the port forward)
Vagrant.configure("2") do |config|
config.vm.box = "precise64"
config.vm.box_url = "http://files.vagrantup.com/precise64.box"
config.vm.network :private_network, ip: "10.10.10.10"
config.ssh.forward_agent = true
config.vm.provider :virtualbox do |v|
v.customize ["modifyvm", :id, "--natdnshostresolver1", "on"]
v.customize ["modifyvm", :id, "--memory", 1024]
v.customize ["modifyvm", :id, "--name", "NGINX_PostgreSQL"]
end
config.vm.synced_folder "./", "/var/www", id: "vagrant-root"
config.vm.provision :shell, :inline =>
"if [[ ! -f /apt-get-run ]]; then sudo apt-get update && sudo touch /apt-get-run; fi"
config.vm.provision :puppet do |puppet|
puppet.manifests_path = "vagrant/manifests"
puppet.module_path = "vagrant/modules"
puppet.options = ['--verbose']
end
end
Here's my default.pp file
group { 'puppet': ensure => present }
Exec { path => [ '/bin/', '/sbin/', '/usr/bin/', '/usr/sbin/' ] }
File { owner => 0, group => 0, mode => 0644 }
class {'apt':
always_apt_update => true,
}
Class['::apt::update'] -> Package <|
title != 'python-software-properties'
and title != 'software-properties-common'
|>
apt::key { '4F4EA0AAE5267A6C': }
apt::ppa { 'ppa:ondrej/php5-oldstable':
require => Apt::Key['4F4EA0AAE5267A6C']
}
class { 'puphpet::dotfiles': }
package { [
'build-essential',
'vim',
'curl',
'git-core'
]:
ensure => 'installed',
}
class { 'nginx': }
nginx::resource::vhost { 'mylaravel.com':
ensure => present,
server_name => [
'mylaravel.com' ],
listen_port => 80,
index_files => [
'index.html',
'index.htm',
'index.php'
],
www_root => '/var/www/public',
try_files => ['$uri', '$uri/', '/index.php?$args'],
}
$path_translated = 'PATH_TRANSLATED $document_root$fastcgi_path_info'
$script_filename = 'SCRIPT_FILENAME $document_root$fastcgi_script_name'
nginx::resource::location { 'mylaravel.com-php':
ensure => 'present',
vhost => 'mylaravel.com',
location => '~ \.php$',
proxy => undef,
try_files => ['$uri', '$uri/', '/index.php?$args'],
www_root => '/var/www/public',
location_cfg_append => {
'fastcgi_split_path_info' => '^(.+\.php)(/.+)$',
'fastcgi_param' => 'PATH_INFO $fastcgi_path_info',
'fastcgi_param ' => $path_translated,
'fastcgi_param ' => $script_filename,
'fastcgi_param ' => 'APP_ENV dev',
'fastcgi_param ' => 'APP_DBG true',
'fastcgi_pass' => 'unix:/var/run/php5-fpm.sock',
'fastcgi_index' => 'index.php',
'include' => 'fastcgi_params'
},
notify => Class['nginx::service'],
}
class { 'php':
package => 'php5-fpm',
service => 'php5-fpm',
service_autorestart => false,
config_file => '/etc/php5/fpm/php.ini',
module_prefix => ''
}
php::module {
[
'php5-pgsql',
'php5-cli',
'php5-curl',
'php5-intl',
'php5-mcrypt',
'php-apc',
]:
service => 'php5-fpm',
}
service { 'php5-fpm':
ensure => running,
enable => true,
hasrestart => true,
hasstatus => true,
require => Package['php5-fpm'],
}
class { 'php::devel':
require => Class['php'],
}
class { 'xdebug':
service => 'nginx',
}
class { 'composer':
require => Package['php5-fpm', 'curl'],
}
puphpet::ini { 'xdebug':
value => [
'xdebug.default_enable = 1',
'xdebug.remote_autostart = 0',
'xdebug.remote_connect_back = 1',
'xdebug.remote_enable = 1',
'xdebug.remote_handler = "dbgp"',
'xdebug.remote_port = 9000'
],
ini => '/etc/php5/conf.d/zzz_xdebug.ini',
notify => Service['php5-fpm'],
require => Class['php'],
}
puphpet::ini { 'php':
value => [
'date.timezone = "America/Chicago"'
],
ini => '/etc/php5/conf.d/zzz_php.ini',
notify => Service['php5-fpm'],
require => Class['php'],
}
puphpet::ini { 'custom':
value => [
'display_errors = On',
'error_reporting = -1'
],
ini => '/etc/php5/conf.d/zzz_custom.ini',
notify => Service['php5-fpm'],
require => Class['php'],
}
class { 'postgresql':
charset => 'UTF8',
locale => 'en_US.UTF-8',
}->
class { 'postgresql::server':
config_hash => {
postgres_password => 'root',
},
}
postgresql::db { 'appDB':
user => 'dadams',
password => 'mypassword',
grant => 'ALL',
}
and here's my pg_hba.conf file inside the VM
# This file is managed by Puppet. DO NOT EDIT.
# Rule Name: local access as postgres user
# Description: none
# Order: 001
local all postgres ident
# Rule Name: local access to database with same name
# Description: none
# Order: 002
local all all ident
# Rule Name: deny access to postgresql user
# Description: none
# Order: 003
host all postgres 0.0.0.0/0 reject
# Rule Name: allow access to all users
# Description: none
# Order: 100
host all all 127.0.0.1/32 md5
# Rule Name: allow access to ipv6 localhost
# Description: none
# Order: 101
host all all ::1/128 md5
Most GUIs will allow you to connect via an SSH tunnel. This is the best way to do what you want.
Add the following port forwarding rule in Vagrantfile and do a vagrant reload, see if you can connect to the postgresql.
config.vm.network :forwarded_port, guest: 5432, host:5432
NOTE: you may still need to change postgresql.conf listen_addresses (bind) to * (all) interfaces and allow client connections from certain networks by modifying host records in the pg_hba.conf file.
Sample allow connection from network 10.1.1.0/24 unconditionally
host all all 10.1.1.0/24 trust
I think in your use case, enabling a 2nd network interface (public network) will make life easier, avoid lots of port forwarding and network issue.

Openid - User details after authentication

I am trying to use Catalyst::Authentication::Credential::OpenID to authenticate users from Google.
Once authentication is successful, I get a Catalyst::Plugin::Authentication::User::Hash object as my user.
If users are logging in for the first time in my application, I want to get details of user from OpenID provider and store them in my DB.
This is to ease the process of registration, I want as much details from OpenID as possible.
But at least first name, last name, email etc..
But I am not able to achieve it. As an example, if I call, I get exception saying method *url,display * are not defined.
$c->user->url
$c->user->display
Any help in sorting it out is helpful.
After reading the Catalyst manual a number of times and getting some clue from Catalyst mailing lists, I came to know that we have to use extensions.
Because we will be using a number of different realms, I used progressive class.
Here is sample configuration used in my app, currently supporting only openID.
This uses Simple Registration Schema for OpenID Attribute Exchange defined at
http://www.axschema.org/types/
'Plugin::Authentication' => {
default_realm => 'progressive',
realms => {
progressive => {
class => 'Progressive',
realms => [ 'openid' ],
},
openid => {
credential => {
class => "OpenID",
store => {
class => "OpenID",
},
consumer_secret => "Don't bother setting",
ua_class => "LWP::UserAgent",
# whitelist is only relevant for LWPx::ParanoidAgent
ua_args => {
whitelisted_hosts => [qw/ 127.0.0.1 localhost /],
},
extensions => [
'http://openid.net/srv/ax/1.0' => {
mode => 'fetch_request',
'type.nickname' => 'http://axschema.org/namePerson/friendly',
'type.email' => 'http://axschema.org/contact/email',
'type.fullname' => 'http://axschema.org/namePerson',
'type.firstname' => 'http://axschema.org/namePerson/first',
'type.lastname' => 'http://axschema.org/namePerson/last',
'type.dob' => 'http://axschema.org/birthDate',
'type.gender' => 'http://axschema.org/person/gender',
'type.country' => 'http://axschema.org/contact/country/home',
'type.language' => 'http://axschema.org/pref/language',
'type.timezone' => 'http://axschema.org/pref/timezone',
required => 'nickname,fullname,email,firstname,lastname,dob,gender,country',
if_available => 'dob,gender,language,timezone',
}
],
},
}
}
},