File Coverage

blib/lib/Net/OpenStack/Client/Auth.pm
Criterion Covered Total %
statement 57 71 80.2
branch 10 26 38.4
condition 1 3 33.3
subroutine 8 8 100.0
pod 3 3 100.0
total 79 111 71.1


line stmt bran cond sub pod time code
1             package Net::OpenStack::Client::Auth;
2             $Net::OpenStack::Client::Auth::VERSION = '0.1.4';
3 7     7   3032 use strict;
  7         14  
  7         183  
4 7     7   32 use warnings;
  7         13  
  7         152  
5              
6 7     7   32 use Readonly;
  7         10  
  7         6071  
7              
8             Readonly my $OPENRC_REQUIRED => [qw(username password auth_url)];
9              
10             Readonly my %OPENRC_DEFAULT => {
11             identity_api_version => 3,
12             project_domain_name => 'Default',
13             user_domain_name => 'Default',
14             };
15              
16             Readonly my $DEFAULT_ENDPOINT_INTERFACE_PREFERENCE => [qw(admin internal public)];
17              
18             # read openrc file and extract the variables in hashref
19             sub _parse_openrc
20             {
21 4     4   351 my ($self, $fn) = @_;
22              
23 4         6 my $res;
24 4 50       143 if (open(my $fh, $fn)) {
25 4         75 while (<$fh>) {
26 32         47 chomp;
27 32 50       122 if (m/^\s*(?:export\s+)(\w+)\s*=\s*(['"]?)(.+)\2\s*$/) {
28 32         149 $res->{$1} = $3;
29             }
30             }
31 4         34 close($fh);
32 4         78 $self->debug("Parsed openrc file $fn: found variables ".join(',', sort keys %$res));
33             } else {
34 0         0 $self->error("Failed to openrc file $fn: $!");
35             }
36 4         1282 return $res;
37             }
38              
39              
40             =head1 methods
41              
42             =over
43              
44             =item get_openrc
45              
46             Given variable name, get OS_ from hashref C.
47              
48             Use default from OPENRC_DEFAULT, if none exists.
49             If none exists, and no default and in OPENRC_REQUIRED, report error.
50              
51             =cut
52              
53             sub get_openrc
54             {
55 21     21 1 33 my ($self, $var, $data) = @_;
56              
57 21         39 my $full_var = "OS_".uc($var);
58 21 50       37 if (exists($data->{$full_var})) {
    0          
59 21         94 return $data->{$full_var};
60             } elsif (exists($OPENRC_DEFAULT{$full_var})) {
61 0         0 return $OPENRC_DEFAULT{$full_var};
62             } else {
63 0 0       0 my $req = (grep {$_ eq $var} @$OPENRC_REQUIRED) ? 1 : 0;
  0         0  
64 0 0       0 my $method = $req ? 'error' : 'debug';
65 0 0       0 $self->$method("openrc ".($req ? 'required ' : '')."variable $var ($full_var) not found");
66             }
67              
68 0         0 return;
69             }
70              
71              
72             =item login
73              
74             Login and obtain token for further authentication.
75              
76             Options:
77              
78             =over
79              
80             =item openrc: openrc file to parse to extract the login details.
81              
82              
83             =back
84              
85             =cut
86              
87             sub login
88             {
89 3     3 1 10 my ($self, %opts) = @_;
90              
91 3 50       11 if ($opts{openrc}) {
92             my $openrc = $self->_parse_openrc($opts{openrc})
93 3 50       14 or return;
94              
95 3     21   15 my $os = sub {return $self->get_openrc(shift, $openrc)};
  21         49  
96              
97 3         15 my $version = version->new('v'.&$os('identity_api_version'));
98 3         11 $self->{versions}->{identity} = $version;
99 3 50       26 if ($self->{versions}->{identity} == 3) {
100 3         7 $self->{services}->{identity} = &$os('auth_url');
101 3   33     9 my $opts = {
102             methods => ['password'],
103             user_name => &$os('username'),
104             password => &$os('password'),
105             user_domain_name => &$os('user_domain_name') || &$os('project_domain_name'),
106             project_domain_name => &$os('project_domain_name'),
107             project_name => &$os('project_name'),
108             };
109              
110 3         13 my $resp = $self->api_identity_tokens(map {$_ => $opts->{$_}} grep {defined($opts->{$_})} keys %$opts);
  18         55  
  18         32  
111 3 50       14 if ($resp) {
112             # token in result attr
113 3         16 $self->{token} = $resp->result;
114 3         11 $self->verbose("login succesful, obtained a token");
115             # parse the catalog
116 3         800 $self->services_from_catalog($resp->{data}->{token}->{catalog});
117             } else {
118 0         0 $self->error("login: failed to get token $resp->{error}");
119 0         0 return;
120             }
121             } else {
122 0         0 $self->error("login: only identity v3 supported for now");
123 0         0 return;
124             }
125             } else {
126 0         0 $self->error("login: only openrc supported for now");
127 0         0 return;
128             }
129              
130 3         272 return 1;
131             }
132              
133             =item services_from_catalog
134              
135             Parse the catalog arrayref, and build up the services attribute
136              
137             =cut
138              
139             sub services_from_catalog
140             {
141 3     3 1 11 my ($self, $catalog) = @_;
142              
143             # TODO: allow to change this
144 3         12 my @pref_intfs = (@$DEFAULT_ENDPOINT_INTERFACE_PREFERENCE);
145              
146 3         54 foreach my $service (@$catalog) {
147 2         248 my $type = $service->{type};
148 2         3 my $endpoint;
149 2         3 foreach my $intf (@pref_intfs) {
150 3         6 my @epts = grep {$_->{interface} eq $intf} @{$service->{endpoints}};
  6         12  
  3         6  
151 3 100       7 if (@epts) {
152 2         3 $endpoint = $epts[0]->{url};
153 2         4 last;
154             }
155             }
156 2         5 my $msg = "for service $type from catalog";
157 2 50       5 if ($endpoint) {
158 2         4 $self->{services}->{$type} = $endpoint;
159 2         9 $self->verbose("Added endpoint $endpoint $msg");
160             } else {
161 0           $self->error("No endpoint $msg using preferred interfaces ".join(",", @pref_intfs));
162             }
163             }
164             }
165              
166             =pod
167              
168             =back
169              
170             =cut
171              
172             1;