Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
S
slapos.core
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
Kirill Smelkov
slapos.core
Commits
4528e8cb
Commit
4528e8cb
authored
Sep 15, 2014
by
Cédric de Saint Martin
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
[slapos.core] Add retention_delay support.
parent
bec27130
Changes
4
Expand all
Hide whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
297 additions
and
29 deletions
+297
-29
slapos/grid/SlapObject.py
slapos/grid/SlapObject.py
+135
-0
slapos/grid/slapgrid.py
slapos/grid/slapgrid.py
+7
-4
slapos/tests/slapgrid.py
slapos/tests/slapgrid.py
+60
-22
slapos/tests/slapobject.py
slapos/tests/slapobject.py
+95
-3
No files found.
slapos/grid/SlapObject.py
View file @
4528e8cb
...
...
@@ -37,6 +37,7 @@ import subprocess
import
tarfile
import
tempfile
import
textwrap
import
time
import
xmlrpclib
from
supervisor
import
xmlrpc
...
...
@@ -273,6 +274,8 @@ class Software(object):
class
Partition
(
object
):
"""This class is responsible of the installation of an instance
"""
retention_lock_delay_filename
=
'.slapos-retention-lock-delay'
retention_lock_date_filename
=
'.slapos-retention-lock-date'
# XXX: we should give the url (or the "key") instead of the software_path
# then compute the path from it, like in Software.
...
...
@@ -289,6 +292,7 @@ class Partition(object):
buildout
,
logger
,
certificate_repository_path
=
None
,
retention_delay
=
0
):
"""Initialisation of class parameters"""
self
.
buildout
=
buildout
...
...
@@ -315,6 +319,20 @@ class Partition(object):
self
.
partition_id
+
'.crt'
)
self
.
_updateCertificate
()
self
.
retention_delay
=
retention_delay
if
type
(
self
.
retention_delay
)
not
in
(
int
,
float
)
\
or
self
.
retention_delay
<=
0
:
self
.
logger
.
warn
(
'Retention delay value (%s) is not valid, ignoring.'
\
%
self
.
retention_delay
)
self
.
retention_delay
=
0
self
.
retention_lock_delay_file_path
=
os
.
path
.
join
(
self
.
instance_path
,
self
.
retention_lock_delay_filename
)
self
.
retention_lock_date_file_path
=
os
.
path
.
join
(
self
.
instance_path
,
self
.
retention_lock_date_filename
)
def
_updateCertificate
(
self
):
try
:
partition_certificate
=
self
.
computer_partition
.
getCertificate
()
...
...
@@ -492,6 +510,7 @@ class Partition(object):
buildout_binary
=
buildout_binary
,
logger
=
self
.
logger
)
self
.
generateSupervisorConfigurationFile
()
self
.
createRetentionLockDelay
()
def
generateSupervisorConfigurationFile
(
self
):
"""
...
...
@@ -564,6 +583,11 @@ class Partition(object):
"""
self
.
logger
.
info
(
"Destroying Computer Partition %s..."
%
self
.
computer_partition
.
getId
())
self
.
createRetentionLockDate
()
if
not
self
.
checkRetentionIsAuthorized
():
return
False
# Launches "destroy" binary if exists
destroy_executable_location
=
os
.
path
.
join
(
self
.
instance_path
,
'sbin'
,
'destroy'
)
...
...
@@ -607,6 +631,8 @@ class Partition(object):
except
IOError
as
exc
:
raise
IOError
(
"I/O error while freeing partition (%s): %s"
%
(
self
.
instance_path
,
exc
))
return
True
def
fetchInformations
(
self
):
"""Fetch usage informations with buildout, returns it.
"""
...
...
@@ -649,3 +675,112 @@ class Partition(object):
supervisor
.
addProcessGroup
(
gname
)
self
.
logger
.
info
(
'Updated %r'
%
gname
)
self
.
logger
.
debug
(
'Supervisord updated'
)
def
_set_ownership
(
self
,
path
):
"""
If running as root: copy ownership of software_path to path
If not running as root: do nothing
"""
if
os
.
getuid
():
return
root_stat
=
os
.
stat
(
self
.
software_path
)
path_stat
=
os
.
stat
(
path
)
if
(
root_stat
.
st_uid
!=
path_stat
.
st_uid
or
root_stat
.
st_gid
!=
path_stat
.
st_gid
):
os
.
chown
(
path
,
root_stat
.
st_uid
,
root_stat
.
st_gid
)
def
checkRetentionIsAuthorized
(
self
):
"""
Check if retention is authorized by checking retention lock delay or
retention lock date.
A retention lock delay is a delay which is:
* Defined by the user/machine who requested the instance
* Hardcoded the first time the instance is deployed, then is read-only
during the whole lifetime of the instance
* Triggered the first time the instance is requested to be destroyed
(retention will be ignored).
From this point, it is not possible to destroy the instance until the
delay is over.
* Accessible in read-only mode from the partition
A retention lock date is the date computed from (date of first
retention request + retention lock delay in days).
Example:
* User requests an instance with delay as 10 (days) to a SlapOS Master
* SlapOS Master transmits this information to the SlapOS Node (current code)
* SlapOS Node hardcodes this delay at first deployment
* User requests retention of instance
* SlapOS Node tries to destroy for the first time: it doesn't actually
destroy, but it triggers the creation of a retention lock date from
from the hardcoded delay. At this point it is not possible to
destroy instance until current date + 10 days.
* SlapOS Node continues to try to destroy: it doesn't do anything until
retention lock date is reached.
"""
retention_lock_date
=
self
.
getExistingRetentionLockDate
()
now
=
time
.
time
()
if
not
retention_lock_date
:
if
self
.
getExistingRetentionLockDelay
()
>
0
:
self
.
logger
.
info
(
'Impossible to destroy partition yet because of retention lock.'
)
return
False
# Else: OK to destroy
else
:
if
now
<
retention_lock_date
:
self
.
logger
.
info
(
'Impossible to destroy partition yet because of retention lock.'
)
return
False
# Else: OK to destroy
return
True
def
createRetentionLockDelay
(
self
):
"""
Create a retention lock delay for the current partition.
If retention delay is not specified, create it wth "0" as value
"""
if
os
.
path
.
exists
(
self
.
retention_lock_delay_file_path
):
return
with
open
(
self
.
retention_lock_delay_file_path
,
'w'
)
as
delay_file_path
:
delay_file_path
.
write
(
str
(
self
.
retention_delay
))
self
.
_set_ownership
(
self
.
retention_lock_delay_file_path
)
def
getExistingRetentionLockDelay
(
self
):
"""
Return the retention lock delay of current partition (created at first
deployment) if exist.
Return -1 otherwise.
"""
retention_delay
=
-
1
if
os
.
path
.
exists
(
self
.
retention_lock_delay_file_path
):
with
open
(
self
.
retention_lock_delay_file_path
)
as
delay_file_path
:
retention_delay
=
float
(
delay_file_path
.
read
())
return
retention_delay
def
createRetentionLockDate
(
self
):
"""
If retention lock delay > 0:
Create a retention lock date for the current partition from the
retention lock delay.
Do nothing otherwise.
"""
if
os
.
path
.
exists
(
self
.
retention_lock_date_file_path
):
return
retention_delay
=
self
.
getExistingRetentionLockDelay
()
if
retention_delay
<=
0
:
return
now
=
int
(
time
.
time
())
retention_date
=
now
+
retention_delay
*
24
*
3600
with
open
(
self
.
retention_lock_date_file_path
,
'w'
)
as
date_file_path
:
date_file_path
.
write
(
str
(
retention_date
))
self
.
_set_ownership
(
self
.
retention_lock_date_file_path
)
def
getExistingRetentionLockDate
(
self
):
"""
Return the retention lock delay of current partition if exist.
Return None otherwise.
"""
if
os
.
path
.
exists
(
self
.
retention_lock_date_file_path
):
with
open
(
self
.
retention_lock_date_file_path
)
as
date_file_path
:
return
float
(
date_file_path
.
read
())
else
:
return
None
slapos/grid/slapgrid.py
View file @
4528e8cb
...
...
@@ -662,7 +662,8 @@ class Slapgrid(object):
software_release_url
=
software_url
,
certificate_repository_path
=
self
.
certificate_repository_path
,
buildout
=
self
.
buildout
,
logger
=
self
.
logger
)
logger
=
self
.
logger
,
retention_delay
=
getattr
(
computer_partition
,
'_retention_delay'
,
0
))
computer_partition_state
=
computer_partition
.
getState
()
# XXX this line breaks 37 tests
...
...
@@ -1088,7 +1089,8 @@ class Slapgrid(object):
software_release_url
=
software_url
,
certificate_repository_path
=
self
.
certificate_repository_path
,
buildout
=
self
.
buildout
,
logger
=
self
.
logger
)
logger
=
self
.
logger
,
retention_delay
=
getattr
(
computer_partition
,
'_retention_delay'
,
0
))
local_partition
.
stop
()
try
:
computer_partition
.
stopped
()
...
...
@@ -1101,7 +1103,7 @@ class Slapgrid(object):
self
.
logger
.
info
(
'Ignoring destruction of %r, as no report usage was sent'
%
computer_partition
.
getId
())
continue
local_partition
.
destroy
()
destroyed
=
local_partition
.
destroy
()
except
(
SystemExit
,
KeyboardInterrupt
):
computer_partition
.
error
(
traceback
.
format_exc
(),
logger
=
self
.
logger
)
raise
...
...
@@ -1111,7 +1113,8 @@ class Slapgrid(object):
exc
=
traceback
.
format_exc
()
computer_partition
.
error
(
exc
,
logger
=
self
.
logger
)
try
:
computer_partition
.
destroyed
()
if
destroyed
:
computer_partition
.
destroyed
()
except
NotFoundError
:
self
.
logger
.
debug
(
'Ignored slap error while trying to inform about '
'destroying not fully configured Computer Partition %r'
%
...
...
slapos/tests/slapgrid.py
View file @
4528e8cb
This diff is collapsed.
Click to expand it.
slapos/tests/slapobject.py
View file @
4528e8cb
...
...
@@ -27,6 +27,7 @@
import
logging
import
os
import
time
import
unittest
from
slapos.slap
import
ComputerPartition
as
SlapComputerPartition
...
...
@@ -125,7 +126,8 @@ class MasterMixin(BasicMixin, unittest.TestCase):
self
,
software_release_url
,
partition_id
=
None
,
slap_computer_partition
=
None
slap_computer_partition
=
None
,
retention_delay
=
None
,
):
"""
Create a partition, and return a Partition object created
...
...
@@ -153,10 +155,11 @@ class MasterMixin(BasicMixin, unittest.TestCase):
self
.
instance_root
,
'supervisor'
)
os
.
mkdir
(
supervisor_configuration_path
)
return
Partition
(
partition
=
Partition
(
software_path
=
software_path
,
instance_path
=
instance_path
,
supervisord_partition_configuration_path
=
supervisor_configuration_path
,
supervisord_partition_configuration_path
=
os
.
path
.
join
(
supervisor_configuration_path
,
partition_id
),
supervisord_socket
=
os
.
path
.
join
(
supervisor_configuration_path
,
'supervisor.sock'
),
computer_partition
=
slap_computer_partition
,
...
...
@@ -168,6 +171,11 @@ class MasterMixin(BasicMixin, unittest.TestCase):
logger
=
logging
.
getLogger
(),
)
partition
.
updateSupervisor
=
FakeCallAndNoop
if
retention_delay
:
partition
.
retention_delay
=
retention_delay
return
partition
class
TestSoftwareNetworkCacheSlapObject
(
MasterMixin
,
unittest
.
TestCase
):
"""
...
...
@@ -382,3 +390,87 @@ class TestPartitionSlapObject(MasterMixin, unittest.TestCase):
# XXX: What should it raise?
self
.
assertRaises
(
IOError
,
partition
.
install
)
class
TestPartitionDestructionLock
(
MasterMixin
,
unittest
.
TestCase
):
def
setUp
(
self
):
MasterMixin
.
setUp
(
self
)
Partition
.
generateSupervisorConfigurationFile
=
FakeCallAndNoop
()
utils
.
bootstrapBuildout
=
FakeCallAndNoop
()
utils
.
launchBuildout
=
FakeCallAndStore
()
def
test_retention_lock_delay_creation
(
self
):
delay
=
42
software
=
self
.
createSoftware
()
partition
=
self
.
createPartition
(
software
.
url
,
retention_delay
=
delay
)
partition
.
install
()
deployed_delay
=
int
(
open
(
partition
.
retention_lock_delay_file_path
).
read
())
self
.
assertEqual
(
delay
,
deployed_delay
)
def
test_no_retention_lock_delay
(
self
):
software
=
self
.
createSoftware
()
partition
=
self
.
createPartition
(
software
.
url
)
partition
.
install
()
delay
=
open
(
partition
.
retention_lock_delay_file_path
).
read
()
self
.
assertTrue
(
delay
,
'0'
)
self
.
assertTrue
(
partition
.
destroy
())
def
test_retention_lock_delay_does_not_change
(
self
):
delay
=
42
software
=
self
.
createSoftware
()
partition
=
self
.
createPartition
(
software
.
url
,
retention_delay
=
delay
)
partition
.
install
()
partition
.
retention_delay
=
23
# install/destroy many times
partition
.
install
()
partition
.
destroy
()
partition
.
destroy
()
partition
.
install
()
partition
.
destroy
()
deployed_delay
=
int
(
open
(
partition
.
retention_lock_delay_file_path
).
read
())
self
.
assertEqual
(
delay
,
deployed_delay
)
def
test_retention_lock_delay_is_respected
(
self
):
delay
=
2.0
/
(
3600
*
24
)
software
=
self
.
createSoftware
()
partition
=
self
.
createPartition
(
software
.
url
,
retention_delay
=
delay
)
partition
.
install
()
deployed_delay
=
float
(
open
(
partition
.
retention_lock_delay_file_path
).
read
())
self
.
assertEqual
(
int
(
delay
),
int
(
deployed_delay
))
self
.
assertFalse
(
partition
.
destroy
())
time
.
sleep
(
1
)
self
.
assertFalse
(
partition
.
destroy
())
time
.
sleep
(
1
)
self
.
assertTrue
(
partition
.
destroy
())
def
test_retention_lock_date_creation
(
self
):
delay
=
42
software
=
self
.
createSoftware
()
partition
=
self
.
createPartition
(
software
.
url
,
retention_delay
=
delay
)
partition
.
install
()
self
.
assertFalse
(
os
.
path
.
exists
(
partition
.
retention_lock_date_file_path
))
partition
.
destroy
()
deployed_date
=
float
(
open
(
partition
.
retention_lock_date_file_path
).
read
())
self
.
assertEqual
(
delay
*
3600
*
24
+
int
(
time
.
time
()),
int
(
deployed_date
))
def
test_retention_lock_date_does_not_change
(
self
):
delay
=
42
software
=
self
.
createSoftware
()
partition
=
self
.
createPartition
(
software
.
url
,
retention_delay
=
delay
)
now
=
time
.
time
()
partition
.
install
()
partition
.
destroy
()
partition
.
retention_delay
=
23
# install/destroy many times
partition
.
install
()
partition
.
destroy
()
partition
.
destroy
()
partition
.
install
()
partition
.
destroy
()
deployed_date
=
float
(
open
(
partition
.
retention_lock_date_file_path
).
read
())
self
.
assertEqual
(
delay
*
3600
*
24
+
int
(
now
),
int
(
deployed_date
))
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment